From e74964417a530d8925c171af1bf819aac1368760 Mon Sep 17 00:00:00 2001 From: Marc Aymerich Date: Wed, 3 Feb 2016 14:19:50 +0000 Subject: [PATCH] Improved orchestration.server validation logic --- orchestra/contrib/mailboxes/forms.py | 7 +++++ orchestra/contrib/mailboxes/settings.py | 6 +++++ orchestra/contrib/orchestration/models.py | 31 +++++++++++++++++------ orchestra/core/validators.py | 19 ++++++++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/orchestra/contrib/mailboxes/forms.py b/orchestra/contrib/mailboxes/forms.py index cbfbf71c..ae2dd213 100644 --- a/orchestra/contrib/mailboxes/forms.py +++ b/orchestra/contrib/mailboxes/forms.py @@ -44,6 +44,13 @@ class MailboxForm(forms.ModelForm): if self.instance and self.instance.pk: self.fields['addresses'].initial = self.instance.addresses.all() + + def clean_name(self): + name = self.cleaned_data['name'] + max_length = settings.MAILBOXES_NAME_MAX_LENGTH + if len(name) > max_length: + raise ValidationError("Name length should be less than %i" % max_length) + return name class MailboxChangeForm(UserChangeForm, MailboxForm): diff --git a/orchestra/contrib/mailboxes/settings.py b/orchestra/contrib/mailboxes/settings.py index 39403d3e..fc74e56e 100644 --- a/orchestra/contrib/mailboxes/settings.py +++ b/orchestra/contrib/mailboxes/settings.py @@ -20,6 +20,12 @@ MAILBOXES_DOMAIN_MODEL = Setting('MAILBOXES_DOMAIN_MODEL', 'domains.Domain', ) +MAILBOXES_NAME_MAX_LENGTH = Setting('MAILBOXES_NAME_MAX_LENGTH', + 64, + help_text=_("Limit for system user based mailbox on Linux should be 32.") +) + + MAILBOXES_HOME = Setting('MAILBOXES_HOME', '/home/%(name)s', help_text="Available fromat names: %s" % ', '.join(_names), diff --git a/orchestra/contrib/orchestration/models.py b/orchestra/contrib/orchestration/models.py index 8ccc1347..dd14a0e7 100644 --- a/orchestra/contrib/orchestration/models.py +++ b/orchestra/contrib/orchestration/models.py @@ -3,13 +3,14 @@ import socket from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError from django.db import models from django.utils.encoding import force_text from django.utils.functional import cached_property from django.utils.module_loading import autodiscover_modules from django.utils.translation import ugettext_lazy as _ -from orchestra.core.validators import validate_ip_address, ValidationError +from orchestra.core.validators import validate_ip_address, validate_hostname, OrValidator from orchestra.models.fields import NullableCharField, MultiSelectField from . import settings @@ -21,9 +22,13 @@ logger = logging.getLogger(__name__) class Server(models.Model): """ Machine runing daemons (services) """ - name = models.CharField(_("name"), max_length=256, unique=True) + name = models.CharField(_("name"), max_length=256, unique=True, + help_text=_("Verbose name or hostname of this server.")) address = NullableCharField(_("address"), max_length=256, blank=True, - null=True, unique=True, help_text=_("IP address or domain name")) + validators=[OrValidator(validate_ip_address, validate_hostname)], + null=True, unique=True, help_text=_( + "Optional IP address or domain name. Name field will be used if not provided.
" + "If the IP address never change you can set this field and save DNS requests.")) description = models.TextField(_("description"), blank=True) os = models.CharField(_("operative system"), max_length=32, choices=settings.ORCHESTRATION_OS_CHOICES, @@ -38,14 +43,24 @@ class Server(models.Model): return self.name def get_ip(self): - if self.address: - return self.address + address = self.get_address() try: - validate_ip_address(self.name) + return validate_ip_address(address) except ValidationError: return socket.gethostbyname(self.name) - else: - return self.name + + def clean(self): + self.name = self.name.strip() + self.address = self.address.strip() + if self.name and not self.address: + validate = OrValidator(validate_ip_address, validate_hostname) + validate_hostname(self.name) + try: + validate(self.name) + except ValidationError as err: + raise ValidationError({ + 'name': _("Name should be a valid hostname or IP address when address is not provided.") + }) class BackendLog(models.Model): diff --git a/orchestra/core/validators.py b/orchestra/core/validators.py index e9e03cd6..c863cd6e 100644 --- a/orchestra/core/validators.py +++ b/orchestra/core/validators.py @@ -37,6 +37,25 @@ def all_valid(*args): raise ValidationError(errors) +class OrValidator(object): + """ + Run validators with an OR logic + """ + def __init__(self, *validators): + self.validators = validators + + def __call__(self, value): + msg = [] + for validator in self.validators: + try: + validator(value) + except ValidationError as err: + msg.append(str(err)) + else: + return + raise ValidationError(' OR '.join(msg)) + + def validate_ipv4_address(value): msg = _("Not a valid IPv4 address") try: