Improved validation errors
This commit is contained in:
parent
25df6505bb
commit
6864e462bf
34
TODO.md
34
TODO.md
|
@ -12,7 +12,6 @@ TODO ====
|
||||||
|
|
||||||
* add `BackendLog` retry action
|
* add `BackendLog` retry action
|
||||||
* PHPbBckendMiixin with get_php_ini
|
* PHPbBckendMiixin with get_php_ini
|
||||||
* Apache: `IncludeOptional /etc/apache2/extra-vhos[t]/account-site-custom.con[f]`
|
|
||||||
* webmail identities and addresses
|
* webmail identities and addresses
|
||||||
* user.roles.mailbox its awful when combined with addresses:
|
* user.roles.mailbox its awful when combined with addresses:
|
||||||
* address.mailboxes filter by account is crap in admin and api
|
* address.mailboxes filter by account is crap in admin and api
|
||||||
|
@ -180,3 +179,36 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
||||||
* Databases.User add reverse M2M databases widget (like mailbox.addresses)
|
* Databases.User add reverse M2M databases widget (like mailbox.addresses)
|
||||||
|
|
||||||
* One domain zone validation for each save, not one per subdomain, maybe on modeladmin.save_related? prevent save on model_related, and save it on save_related()
|
* One domain zone validation for each save, not one per subdomain, maybe on modeladmin.save_related? prevent save on model_related, and save it on save_related()
|
||||||
|
|
||||||
|
* Change permissions periodically on the web server, to ensure security
|
||||||
|
|
||||||
|
* Apache RLimit ?
|
||||||
|
|
||||||
|
* Patch suexec: if user mismatch, check user belongs to suexecusergroup group
|
||||||
|
|
||||||
|
* fuck suexec http://www.litespeedtech.com/support/forum/threads/solved-cloudlinux-php-lsapi-say-no-to-suexec.5812/
|
||||||
|
|
||||||
|
* http://mail-archives.apache.org/mod_mbox/httpd-dev/201409.mbox/%3C5411FFBE.9050506@loginroot.com%3E ??
|
||||||
|
|
||||||
|
* Root owned logs on user's home ?
|
||||||
|
|
||||||
|
|
||||||
|
* Secondary user home in /home/secondaryuser and simlink to /home/main/webapps/app so it can have private storage?
|
||||||
|
|
||||||
|
* Grant permissions like in webfaction
|
||||||
|
|
||||||
|
|
||||||
|
* Secondaryusers home should be under mainuser home. i.e. /home/mainuser/webapps/seconduser_webapp/
|
||||||
|
* Make one dedicated CGI user for each account only for CGI execution (fpm/fcgid). Different from the files owner, and without W permissions, so attackers can not inject backdors and malware.
|
||||||
|
* In most cases we can prevent the creation of files for the CGI users, preventing attackers to upload and executing PHPShells.
|
||||||
|
* Make main systemuser able to write/read everything on its home, including stuff created by the CGI user and secondary users
|
||||||
|
* Prevent users from accessing other users home while at the same time allow access Apache/fcgid/fpm and secondary users (x)
|
||||||
|
|
||||||
|
* public_html/webapps directory with root owner and permissions
|
||||||
|
|
||||||
|
* resource min max allocation with validation
|
||||||
|
|
||||||
|
* mailman needs both aliases when address_name is provided (default messages and bounces and all)
|
||||||
|
|
||||||
|
* specify field on ValidationError under model.clean() of form.clean(): ValidationError({'bark_volume': ["Must be louder!",]}
|
||||||
|
* And raise ValidationError once at the end collecting all errors at once
|
||||||
|
|
|
@ -17,7 +17,7 @@ def create_account_creation_form():
|
||||||
help_text=_("Designates whether to creates an enabled or disabled related system user. "
|
help_text=_("Designates whether to creates an enabled or disabled related system user. "
|
||||||
"Notice that a related system user will be always created."))
|
"Notice that a related system user will be always created."))
|
||||||
})
|
})
|
||||||
for model, key, kwargs, help_text in settings.ACCOUNTS_CREATE_RELATED:
|
for model, __, kwargs, help_text in settings.ACCOUNTS_CREATE_RELATED:
|
||||||
model = get_model(model)
|
model = get_model(model)
|
||||||
field_name = 'create_%s' % model._meta.model_name
|
field_name = 'create_%s' % model._meta.model_name
|
||||||
label = _("Create %s") % model._meta.verbose_name
|
label = _("Create %s") % model._meta.verbose_name
|
||||||
|
@ -35,9 +35,10 @@ def create_account_creation_form():
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Previous validation error
|
# Previous validation error
|
||||||
return
|
return
|
||||||
|
errors = {}
|
||||||
systemuser_model = Account.main_systemuser.field.rel.to
|
systemuser_model = Account.main_systemuser.field.rel.to
|
||||||
if systemuser_model.objects.filter(username=account.username).exists():
|
if systemuser_model.objects.filter(username=account.username).exists():
|
||||||
raise forms.ValidationError(_("A system user with this name already exists"))
|
errors['username'] = _("A system user with this name already exists.")
|
||||||
for model, key, related_kwargs, __ in settings.ACCOUNTS_CREATE_RELATED:
|
for model, key, related_kwargs, __ in settings.ACCOUNTS_CREATE_RELATED:
|
||||||
model = get_model(model)
|
model = get_model(model)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
|
@ -45,9 +46,13 @@ def create_account_creation_form():
|
||||||
}
|
}
|
||||||
if model.objects.filter(**kwargs).exists():
|
if model.objects.filter(**kwargs).exists():
|
||||||
verbose_name = model._meta.verbose_name
|
verbose_name = model._meta.verbose_name
|
||||||
raise forms.ValidationError(
|
field_name = 'create_%s' % model._meta.model_name
|
||||||
_("A %s with this name already exists") % verbose_name
|
errors[field] = ValidationError(
|
||||||
)
|
_("A %(type)s with this name already exists."),
|
||||||
|
params={'type': verbose_name})
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
def save_model(self, account):
|
def save_model(self, account):
|
||||||
account.save(active_systemuser=self.cleaned_data['enable_systemuser'])
|
account.save(active_systemuser=self.cleaned_data['enable_systemuser'])
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,10 @@ class BillContact(models.Model):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.vat = self.vat.strip()
|
self.vat = self.vat.strip()
|
||||||
self.city = self.city.strip()
|
self.city = self.city.strip()
|
||||||
validators.validate_vat(self.vat, self.country)
|
validators.all_valid({
|
||||||
validators.validate_zipcode(self.zipcode, self.country)
|
'vat': (validators.validate_vat, self.vat, self.country),
|
||||||
|
'zipcode': (validators.validate_zipcode, self.zipcode, self.country)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class BillManager(models.Manager):
|
class BillManager(models.Manager):
|
||||||
|
|
|
@ -66,12 +66,18 @@ class Contact(models.Model):
|
||||||
self.address = self.address.strip()
|
self.address = self.address.strip()
|
||||||
self.city = self.city.strip()
|
self.city = self.city.strip()
|
||||||
self.country = self.country.strip()
|
self.country = self.country.strip()
|
||||||
|
errors = {}
|
||||||
if self.address and not (self.city and self.zipcode and self.country):
|
if self.address and not (self.city and self.zipcode and self.country):
|
||||||
raise ValidationError(_("City, zipcode and country must be provided when address is provided."))
|
errors['__all__'] = _("City, zipcode and country must be provided when address is provided.")
|
||||||
if self.zipcode and not self.country:
|
if self.zipcode and not self.country:
|
||||||
raise ValidationError(_("Country must be provided when zipcode is provided."))
|
errors['country'] = _("Country must be provided when zipcode is provided.")
|
||||||
elif self.zipcode and self.country:
|
elif self.zipcode and self.country:
|
||||||
validators.validate_zipcode(self.zipcode, self.country)
|
try:
|
||||||
|
validators.validate_zipcode(self.zipcode, self.country)
|
||||||
|
except ValidationError, error:
|
||||||
|
errors['zipcode'] = error
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
|
||||||
accounts.register(Contact)
|
accounts.register(Contact)
|
||||||
|
|
|
@ -21,8 +21,9 @@ class CreateDomainAdminForm(forms.ModelForm):
|
||||||
# Fake an account to make django validation happy
|
# Fake an account to make django validation happy
|
||||||
account_model = self.fields['account']._queryset.model
|
account_model = self.fields['account']._queryset.model
|
||||||
cleaned_data['account'] = account_model()
|
cleaned_data['account'] = account_model()
|
||||||
msg = _("An account should be provided for top domain names")
|
raise ValidationError({
|
||||||
raise ValidationError(msg)
|
'account': _("An account should be provided for top domain names."),
|
||||||
|
})
|
||||||
cleaned_data['account'] = top.account
|
cleaned_data['account'] = top.account
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@ class CreateDomainAdminForm(forms.ModelForm):
|
||||||
# self.save_formset(request, form, formset, change=change)
|
# self.save_formset(request, form, formset, change=change)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO do it in admin
|
||||||
class RecordInlineFormSet(forms.models.BaseInlineFormSet):
|
class RecordInlineFormSet(forms.models.BaseInlineFormSet):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
""" Checks if everything is consistent """
|
""" Checks if everything is consistent """
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Domain(models.Model):
|
||||||
help_text=_("Domain or subdomain name."))
|
help_text=_("Domain or subdomain name."))
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||||
related_name='domains', blank=True, help_text=_("Automatically selected for subdomains."))
|
related_name='domains', blank=True, help_text=_("Automatically selected for subdomains."))
|
||||||
top = models.ForeignKey('domains.Domain', null=True, related_name='subdomains', editable=False)
|
top = models.ForeignKey('domains.Domain', null=True, related_name='subdomain_set', editable=False)
|
||||||
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial,
|
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial,
|
||||||
help_text=_("Serial number"))
|
help_text=_("Serial number"))
|
||||||
|
|
||||||
|
@ -41,6 +41,10 @@ class Domain(models.Model):
|
||||||
# don't cache, don't replace by top_id
|
# don't cache, don't replace by top_id
|
||||||
return not bool(self.top)
|
return not bool(self.top)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def subdomains(self):
|
||||||
|
return Domain.objects.filter(name__regex='\.%s$' % self.name)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return 'http://%s' % self.name
|
return 'http://%s' % self.name
|
||||||
|
|
||||||
|
@ -50,7 +54,7 @@ class Domain(models.Model):
|
||||||
|
|
||||||
def get_subdomains(self):
|
def get_subdomains(self):
|
||||||
""" proxy method, needed for input validation, see helpers.domain_for_validation """
|
""" proxy method, needed for input validation, see helpers.domain_for_validation """
|
||||||
return self.origin.subdomains.all()
|
return self.origin.subdomain_set.all()
|
||||||
|
|
||||||
def get_top(self):
|
def get_top(self):
|
||||||
return type(self).get_top_domain(self.name)
|
return type(self).get_top_domain(self.name)
|
||||||
|
@ -140,8 +144,8 @@ class Domain(models.Model):
|
||||||
update = True
|
update = True
|
||||||
super(Domain, self).save(*args, **kwargs)
|
super(Domain, self).save(*args, **kwargs)
|
||||||
if update:
|
if update:
|
||||||
domains = Domain.objects.exclude(pk=self.pk)
|
for domain in self.subdomains.exclude(pk=self.pk):
|
||||||
for domain in domains.filter(name__endswith=self.name):
|
# queryset.update() is not used because we want to trigger backend to delete ex-topdomains
|
||||||
domain.top = self
|
domain.top = self
|
||||||
domain.save(update_fields=['top'])
|
domain.save(update_fields=['top'])
|
||||||
|
|
||||||
|
@ -182,7 +186,7 @@ class Record(models.Model):
|
||||||
""" validates record value based on its type """
|
""" validates record value based on its type """
|
||||||
# validate value
|
# validate value
|
||||||
self.value = self.value.lower().strip()
|
self.value = self.value.lower().strip()
|
||||||
mapp = {
|
choices = {
|
||||||
self.MX: validators.validate_mx_record,
|
self.MX: validators.validate_mx_record,
|
||||||
self.NS: validators.validate_zone_label,
|
self.NS: validators.validate_zone_label,
|
||||||
self.A: validate_ipv4_address,
|
self.A: validate_ipv4_address,
|
||||||
|
@ -192,7 +196,10 @@ class Record(models.Model):
|
||||||
self.SRV: validators.validate_srv_record,
|
self.SRV: validators.validate_srv_record,
|
||||||
self.SOA: validators.validate_soa_record,
|
self.SOA: validators.validate_soa_record,
|
||||||
}
|
}
|
||||||
mapp[self.type](self.value)
|
try:
|
||||||
|
choices[self.type](self.value)
|
||||||
|
except ValidationError, error:
|
||||||
|
raise ValidationError({'value': error})
|
||||||
|
|
||||||
def get_ttl(self):
|
def get_ttl(self):
|
||||||
return self.ttl or settings.DOMAINS_DEFAULT_TTL
|
return self.ttl or settings.DOMAINS_DEFAULT_TTL
|
||||||
|
|
|
@ -45,7 +45,9 @@ class MailboxForm(forms.ModelForm):
|
||||||
filtering = self.cleaned_data['filtering']
|
filtering = self.cleaned_data['filtering']
|
||||||
custom_filtering = self.cleaned_data['custom_filtering']
|
custom_filtering = self.cleaned_data['custom_filtering']
|
||||||
if filtering == self._meta.model.CUSTOM and not custom_filtering:
|
if filtering == self._meta.model.CUSTOM and not custom_filtering:
|
||||||
raise forms.ValidationError(_("You didn't provide any custom filtering"))
|
raise forms.ValidationError({
|
||||||
|
'custom_filtering': _("You didn't provide any custom filtering.")
|
||||||
|
})
|
||||||
return custom_filtering
|
return custom_filtering
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,4 +71,4 @@ class AddressForm(forms.ModelForm):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super(AddressForm, self).clean()
|
cleaned_data = super(AddressForm, self).clean()
|
||||||
if not cleaned_data.get('mailboxes', True) and not cleaned_data['forward']:
|
if not cleaned_data.get('mailboxes', True) and not cleaned_data['forward']:
|
||||||
raise forms.ValidationError(_("Mailboxes or forward address should be provided"))
|
raise forms.ValidationError(_("Mailboxes or forward address should be provided."))
|
||||||
|
|
|
@ -117,7 +117,7 @@ class Transaction(models.Model):
|
||||||
if not self.pk:
|
if not self.pk:
|
||||||
amount = self.bill.transactions.exclude(state=self.REJECTED).amount()
|
amount = self.bill.transactions.exclude(state=self.REJECTED).amount()
|
||||||
if amount >= self.bill.total:
|
if amount >= self.bill.total:
|
||||||
raise ValidationError(_("New transactions can not be allocated for this bill"))
|
raise ValidationError(_("New transactions can not be allocated for this bill."))
|
||||||
|
|
||||||
def mark_as_processed(self):
|
def mark_as_processed(self):
|
||||||
assert self.state == self.WAITTING_PROCESSING
|
assert self.state == self.WAITTING_PROCESSING
|
||||||
|
|
|
@ -39,6 +39,35 @@ class ServiceHandler(plugins.Plugin):
|
||||||
choices = super(ServiceHandler, cls).get_plugin_choices()
|
choices = super(ServiceHandler, cls).get_plugin_choices()
|
||||||
return [('', _("Default"))] + choices
|
return [('', _("Default"))] + choices
|
||||||
|
|
||||||
|
def validate_content_type(self, service):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def validate_match(self, service):
|
||||||
|
if not service.match:
|
||||||
|
raise ValidationError(_("Match should be provided."))
|
||||||
|
try:
|
||||||
|
obj = service.content_type.model_class().objects.all()[0]
|
||||||
|
except IndexError:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
bool(self.matches(obj))
|
||||||
|
except Exception, exception:
|
||||||
|
name = type(exception).__name__
|
||||||
|
message = exception.message
|
||||||
|
raise ValidationError(': '.join((name, message)))
|
||||||
|
|
||||||
|
def validate_metric(self, service):
|
||||||
|
try:
|
||||||
|
obj = service.content_type.model_class().objects.all()[0]
|
||||||
|
except IndexError:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
bool(self.get_metric(obj))
|
||||||
|
except Exception, exception:
|
||||||
|
name = type(exception).__name__
|
||||||
|
message = exception.message
|
||||||
|
raise ValidationError(': '.join((name, message)))
|
||||||
|
|
||||||
def get_content_type(self):
|
def get_content_type(self):
|
||||||
if not self.model:
|
if not self.model:
|
||||||
return self.content_type
|
return self.content_type
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.utils.functional import cached_property
|
||||||
from django.utils.module_loading import autodiscover_modules
|
from django.utils.module_loading import autodiscover_modules
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.core import caches, services, accounts
|
from orchestra.core import caches, services, accounts, validators
|
||||||
from orchestra.core.validators import validate_name
|
from orchestra.core.validators import validate_name
|
||||||
from orchestra.models import queryset
|
from orchestra.models import queryset
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class ContractedPlan(models.Model):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if not self.pk and not self.plan.allow_multiples:
|
if not self.pk and not self.plan.allow_multiples:
|
||||||
if ContractedPlan.objects.filter(plan=self.plan, account=self.account).exists():
|
if ContractedPlan.objects.filter(plan=self.plan, account=self.account).exists():
|
||||||
raise ValidationError("A contracted plan for this account already exists")
|
raise ValidationError("A contracted plan for this account already exists.")
|
||||||
|
|
||||||
|
|
||||||
class RateQuerySet(models.QuerySet):
|
class RateQuerySet(models.QuerySet):
|
||||||
|
@ -243,33 +243,11 @@ class Service(models.Model):
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.description = self.description.strip()
|
self.description = self.description.strip()
|
||||||
content_type = self.handler.get_content_type()
|
validators.all_valid({
|
||||||
if self.content_type != content_type:
|
'content_type': (self.handler.validate_content_type, self),
|
||||||
ct = str(content_type)
|
'match': (self.handlers.validate_match, self),
|
||||||
raise ValidationError(_("Content type must be equal to '%s'.") % ct)
|
'metric': (self.handlers.validate_metric, self),
|
||||||
if not self.match:
|
})
|
||||||
raise ValidationError(_("Match should be provided"))
|
|
||||||
try:
|
|
||||||
obj = content_type.model_class().objects.all()[0]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
attr = None
|
|
||||||
try:
|
|
||||||
bool(self.handler.matches(obj))
|
|
||||||
except Exception as exception:
|
|
||||||
attr = "Matches"
|
|
||||||
try:
|
|
||||||
metric = self.handler.get_metric(obj)
|
|
||||||
if metric is not None:
|
|
||||||
int(metric)
|
|
||||||
except Exception as exception:
|
|
||||||
attr = "Get metric"
|
|
||||||
if attr is not None:
|
|
||||||
name = type(exception).__name__
|
|
||||||
message = exception.message
|
|
||||||
msg = "{0} {1}: {2}".format(attr, name, message)
|
|
||||||
raise ValidationError(msg)
|
|
||||||
|
|
||||||
def get_pricing_period(self):
|
def get_pricing_period(self):
|
||||||
if self.pricing_period == self.BILLING_PERIOD:
|
if self.pricing_period == self.BILLING_PERIOD:
|
||||||
|
|
|
@ -54,7 +54,7 @@ class WebAppServiceMixin(object):
|
||||||
return {
|
return {
|
||||||
'user': webapp.get_username(),
|
'user': webapp.get_username(),
|
||||||
'group': webapp.get_groupname(),
|
'group': webapp.get_groupname(),
|
||||||
'app_name': webapp.name,
|
'app_name': webapp.get_name(),
|
||||||
'type': webapp.type,
|
'type': webapp.type,
|
||||||
'app_path': webapp.get_path().rstrip('/'),
|
'app_path': webapp.get_path().rstrip('/'),
|
||||||
'banner': self.get_banner(),
|
'banner': self.get_banner(),
|
||||||
|
|
|
@ -77,8 +77,13 @@ class WebAppOption(models.Model):
|
||||||
""" validates name and value according to WEBAPPS_OPTIONS """
|
""" validates name and value according to WEBAPPS_OPTIONS """
|
||||||
__, regex = settings.WEBAPPS_OPTIONS[self.name]
|
__, regex = settings.WEBAPPS_OPTIONS[self.name]
|
||||||
if not re.match(regex, self.value):
|
if not re.match(regex, self.value):
|
||||||
msg = _("'%s' does not match %s")
|
raise ValidationError({
|
||||||
raise ValidationError(msg % (self.value, regex))
|
'value': ValidationError(_("'%(value)s' does not match %(regex)s."),
|
||||||
|
params={
|
||||||
|
'value': self.value,
|
||||||
|
'regex': regex
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
services.register(WebApp)
|
services.register(WebApp)
|
||||||
|
|
|
@ -24,7 +24,7 @@ WEBAPPS_PHPFPM_POOL_PATH = getattr(settings, 'WEBAPPS_PHPFPM_POOL_PATH',
|
||||||
|
|
||||||
|
|
||||||
WEBAPPS_FCGID_PATH = getattr(settings, 'WEBAPPS_FCGID_PATH',
|
WEBAPPS_FCGID_PATH = getattr(settings, 'WEBAPPS_FCGID_PATH',
|
||||||
'/home/httpd/fcgid/%(user)s-%(app_name)s-wrapper')
|
'/home/httpd/fcgid/%(user)s/%(app_name)s-wrapper')
|
||||||
|
|
||||||
|
|
||||||
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', {
|
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', {
|
||||||
|
@ -190,7 +190,7 @@ WEBAPPS_OPTIONS = getattr(settings, 'WEBAPPS_OPTIONS', {
|
||||||
),
|
),
|
||||||
'PHP-suhosin.executor.include.whitelist': (
|
'PHP-suhosin.executor.include.whitelist': (
|
||||||
_("PHP - suhosin.executor.include.whitelist"),
|
_("PHP - suhosin.executor.include.whitelist"),
|
||||||
r'^(upload|phar)$'
|
r'.*$'
|
||||||
),
|
),
|
||||||
'PHP-upload_max_filesize': (
|
'PHP-upload_max_filesize': (
|
||||||
_("PHP - upload_max_filesize"),
|
_("PHP - upload_max_filesize"),
|
||||||
|
|
|
@ -37,6 +37,7 @@ class Apache2Backend(ServiceController):
|
||||||
SuexecUserGroup {{ user }} {{ group }}\
|
SuexecUserGroup {{ user }} {{ group }}\
|
||||||
{% for line in extra_conf.splitlines %}
|
{% for line in extra_conf.splitlines %}
|
||||||
{{ line | safe }}{% endfor %}
|
{{ line | safe }}{% endfor %}
|
||||||
|
IncludeOptional /etc/apache2/extra-vhos[t]/{{ site_unique_name }}.con[f]
|
||||||
</VirtualHost>"""
|
</VirtualHost>"""
|
||||||
))
|
))
|
||||||
apache_conf = apache_conf.render(Context(context))
|
apache_conf = apache_conf.render(Context(context))
|
||||||
|
|
|
@ -82,8 +82,13 @@ class WebsiteOption(models.Model):
|
||||||
""" validates name and value according to WEBSITES_WEBSITEOPTIONS """
|
""" validates name and value according to WEBSITES_WEBSITEOPTIONS """
|
||||||
__, regex = settings.WEBSITES_OPTIONS[self.name]
|
__, regex = settings.WEBSITES_OPTIONS[self.name]
|
||||||
if not re.match(regex, self.value):
|
if not re.match(regex, self.value):
|
||||||
msg = _("'%s' does not match %s")
|
raise ValidationError({
|
||||||
raise ValidationError(msg % (self.value, regex))
|
'value': ValidationError(_("'%(value)s' does not match %(regex)s."),
|
||||||
|
params={
|
||||||
|
'value': self.value,
|
||||||
|
'regex': regex
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class Content(models.Model):
|
class Content(models.Model):
|
||||||
|
|
|
@ -12,6 +12,18 @@ from IPy import IP
|
||||||
from ..utils.python import import_class
|
from ..utils.python import import_class
|
||||||
|
|
||||||
|
|
||||||
|
def all_valid(kwargs):
|
||||||
|
""" helper function to merge multiple validators at once """
|
||||||
|
errors = {}
|
||||||
|
for field, validator in kwargs.iteritems():
|
||||||
|
try:
|
||||||
|
validator[0](*validator[1:])
|
||||||
|
except ValidationError, error:
|
||||||
|
errors[field] = error
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
|
||||||
def validate_ipv4_address(value):
|
def validate_ipv4_address(value):
|
||||||
msg = _("%s is not a valid IPv4 address") % value
|
msg = _("%s is not a valid IPv4 address") % value
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue