X-Git-Url: http://git.ithinksw.org/philo.git/blobdiff_plain/7515976d9cc927f6f72a191442c5788856f610fd..96926ace045422f0f5f1f474a174ebbdc253a023:/validators.py diff --git a/validators.py b/validators.py index 1e73c0b..8b39abd 100644 --- a/validators.py +++ b/validators.py @@ -1,83 +1,18 @@ -from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ from django.core.validators import RegexValidator +from django.core.exceptions import ValidationError +from django.template import Template, Parser, Lexer, TOKEN_BLOCK, TOKEN_VAR, TemplateSyntaxError +from django.utils import simplejson as json import re +from philo.utils import LOADED_TEMPLATE_ATTR -class TreeParentValidator(object): - """ - constructor takes instance and parent_attr, where instance is the model - being validated and parent_attr is where to look on that parent for the - comparison. - """ - #message = _("A tree element can't be its own parent.") - code = 'invalid' - - def __init__(self, instance, parent_attr=None, message=None, code=None): - self.instance = instance - self.parent_attr = parent_attr - self.static_message = message - if code is not None: - self.code = code - - def __call__(self, value): - """ - Validates that the self.instance is not found in the parent tree of - the node given as value. - """ - parent = value - - while parent: - comparison=self.get_comparison(parent) - if comparison == self.instance: - # using (self.message, code=self.code) results in the admin interface - # screwing with the error message and making it be 'Enter a valid value' - raise ValidationError(self.message) - parent=parent.parent - - def get_comparison(self, parent): - if self.parent_attr and hasattr(parent, self.parent_attr): - return getattr(parent, self.parent_attr) - - return parent - - def get_message(self): - return self.static_message or _(u"A %s can't be its own parent." % self.instance.__class__.__name__) - message = property(get_message) - - -class TreePositionValidator(object): - code = 'invalid' - - def __init__(self, parent, slug, obj_class, message=None, code=None): - self.parent = parent - self.slug = slug - self.obj_class = obj_class - self.static_message = message - - if code is not None: - self.code = code - - def __call__(self, value): - """ - Validates that there is no obj of obj_class with the same position - as the compared obj (value) but a different id. - """ - if not isinstance(value, self.obj_class): - raise ValidationError(_(u"The value must be an instance of %s." % self.obj_class.__name__)) - - try: - obj = self.obj_class.objects.get(slug=self.slug, parent=self.parent) - - if obj.id != value.id: - raise ValidationError(self.message) - - except self.obj_class.DoesNotExist: - pass - - def get_message(self): - return self.static_message or _(u"A %s with that path (parent and slug) already exists." % self.obj_class.__name__) - message = property(get_message) +INSECURE_TAGS = ( + 'load', + 'extends', + 'include', + 'debug', +) class RedirectValidator(RegexValidator): @@ -105,3 +40,101 @@ class URLLinkValidator(RegexValidator): r'|)' # also allow internal links r'(?:/?|[/?#]?\S+)$', re.IGNORECASE) message = _(u'Enter a valid absolute or relative redirect target') + + +def json_validator(value): + try: + json.loads(value) + except Exception, e: + raise ValidationError(u'JSON decode error: %s' % e) + + +class TemplateValidationParser(Parser): + def __init__(self, tokens, allow=None, disallow=None, secure=True): + super(TemplateValidationParser, self).__init__(tokens) + + allow, disallow = set(allow or []), set(disallow or []) + + if secure: + disallow |= set(INSECURE_TAGS) + + self.allow, self.disallow, self.secure = allow, disallow, secure + + def parse(self, parse_until=None): + if parse_until is None: + parse_until = [] + + nodelist = self.create_nodelist() + while self.tokens: + token = self.next_token() + # We only need to parse var and block tokens. + if token.token_type == TOKEN_VAR: + if not token.contents: + self.empty_variable(token) + + filter_expression = self.compile_filter(token.contents) + var_node = self.create_variable_node(filter_expression) + self.extend_nodelist(nodelist, var_node,token) + elif token.token_type == TOKEN_BLOCK: + if token.contents in parse_until: + # put token back on token list so calling code knows why it terminated + self.prepend_token(token) + return nodelist + + try: + command = token.contents.split()[0] + except IndexError: + self.empty_block_tag(token) + + if (self.allow and command not in self.allow) or (self.disallow and command in self.disallow): + self.disallowed_tag(command) + + self.enter_command(command, token) + + try: + compile_func = self.tags[command] + except KeyError: + self.invalid_block_tag(token, command, parse_until) + + try: + compiled_result = compile_func(self, token) + except TemplateSyntaxError, e: + if not self.compile_function_error(token, e): + raise + + self.extend_nodelist(nodelist, compiled_result, token) + self.exit_command() + + if parse_until: + self.unclosed_block_tag(parse_until) + + return nodelist + + def disallowed_tag(self, command): + if self.secure and command in INSECURE_TAGS: + raise ValidationError('Tag "%s" is not permitted for security reasons.' % command) + raise ValidationError('Tag "%s" is not permitted here.' % command) + + +class TemplateValidator(object): + def __init__(self, allow=None, disallow=None, secure=True): + self.allow = allow + self.disallow = disallow + self.secure = secure + + def __call__(self, value): + try: + self.validate_template(value) + except ValidationError: + raise + except Exception, e: + raise ValidationError("Template code invalid. Error was: %s: %s" % (e.__class__.__name__, e)) + + def validate_template(self, template_string): + # We want to tokenize like normal, then use a custom parser. + lexer = Lexer(template_string, None) + tokens = lexer.tokenize() + parser = TemplateValidationParser(tokens, self.allow, self.disallow, self.secure) + + for node in parser.parse(): + template = getattr(node, LOADED_TEMPLATE_ATTR, None) \ No newline at end of file