Advertisement
Guest User

Untitled

a guest
Jul 7th, 2015
176
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.12 KB | None | 0 0
  1. from django.template import Template
  2.  
  3.  
  4. class SafeTemplate(Template):
  5. """
  6. Subclass of the regular django Template but disallows rendering anything
  7. that will call a method on a class thus making it safe for use as a user
  8. editable object
  9. Examples:
  10. # this will return the template as expected
  11. >> template = SafeTemplate('{{ server.hostname }}', server=server)
  12. >> template.render(context)
  13. 'reina-1'
  14. # but since this calls a method on server, it will return an empty
  15. # string
  16. >> template = SafeTemplate('{{ server.power_off }}', server=server)
  17. >> template.render(context)
  18. ''
  19. Discussion:
  20. Django offers a way of specifying if a method should not be called
  21. during template rendering. See alters_data in the django docs which is
  22. an attr that can be set on any class method. However, django defaults
  23. this attr to False for all methods. If we were to use this route of
  24. solving the problem at hand, we would have to set this attr all over
  25. the place. That's not reasonable.
  26. Allowed: model fields, custom fields, and properties on a server
  27. Disallowed: all methods on django's RelatedManager objects, all things
  28. that return true for inspect.ismethod = True
  29. Eventually it would be nice to include a white/blacklist to speed this
  30. process up
  31. """
  32.  
  33. def render(self, context, autoescape=True):
  34. """
  35. Rebuild the template.nodelist to filter out any Nodes we don't like
  36. This accepts any TextNode types, conditionally accepts any VariableNode
  37. types, and rejects all other node types (ex: comments, forloops, etc)
  38. """
  39. final_nodelist = DebugNodeList()
  40. for node in self.nodelist:
  41.  
  42. if isinstance(node, VariableNode):
  43. # this is some sort of variable call so we need to confirm that
  44. # its allowable
  45. lookups = node.filter_expression.var.lookups
  46. if not self.alters_data(lookups, context):
  47. final_nodelist.append(node)
  48.  
  49. elif isinstance(node, TextNode):
  50. # this is just text so we'll allow it
  51. final_nodelist.append(node)
  52.  
  53. self.nodelist = final_nodelist
  54. rendered_data = super(SafeTemplate, self).render(context)
  55.  
  56. if not autoescape:
  57. # When rendering a template, django autoescapes by default. This
  58. # means that all quotation marks, greaterthan/lessthan signs, etc
  59. # will be escaped. In some cases we may not want this (ex. if this
  60. # is a script to be run on a server).
  61. rendered_data = HTMLParser().unescape(rendered_data)
  62.  
  63. return rendered_data
  64.  
  65. def alters_data(self, var_lookups, context):
  66. """
  67. Determine if the list of var_lookups would produce a method call,
  68. return True if so
  69. `var_lookups` is a tuple of strings as returned by
  70. node.filter_expression.var.lookups (see render() above). This list is
  71. basically the template node's string value split at '.'
  72. Examples:
  73. When var_lookups = ('server', 'hostname'), the django template
  74. rendering would call server.hostname which we will allow
  75. When var_lookups = ('server', 'environment', 'server_set', 'all',
  76. 'delete') the user is trying to delete all servers from this
  77. environment. Since calling `all` is a method, it is disallowed
  78. """
  79. # The first lookup will be an object found in the context dictionary
  80. this_lookup = context[var_lookups[0]]
  81.  
  82. for lookup in var_lookups[1:]:
  83.  
  84. try:
  85. attr = this_lookup.__getattribute__(lookup)
  86. except AttributeError:
  87. # If this is a custom field on a server, then __getattribute__
  88. # throws an AttributeError. Therefore, we must call
  89. # get_value_for_custom_field directly.
  90. attr = this_lookup.get_value_for_custom_field(lookup)
  91.  
  92. if inspect.ismethod(attr):
  93. return True
  94. this_lookup = attr
  95.  
  96. return False
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement