示例#1
0
 def compile_selector(self, expr, default_type):
     """
     Compiles a single selector string to ``(selector_type,
     selector_object, expression_string, attributes)`` where the
     selector_type is a string (``"elements"``, ``"children"``,
     etc), selector_object is a callable that returns elements,
     expression_string is the original expression, passed in, and
     ``attributes`` is a list of attributes in the case of
     ``attributes(attr1, attr2):``
     """
     type, attributes, rest_expr = self.parse_prefix(
         expr, default_type=default_type)
     if not self.types_compatible(type, self.major_type):
         raise DeliveranceSyntaxError(
             "Expression %s in selector %r uses the type %r, but this is not "
             "compatible with the type %r already declared earlier in the selector"
             % (expr, self, type, self.major_type))
     if rest_expr.startswith('/'):
         selector = XPath(rest_expr)
     else:
         try:
             selector = CSSSelector(rest_expr)
         except AssertionError as e:
             raise DeliveranceSyntaxError('Bad CSS selector: "%s" (%s)' %
                                          (expr, e))
     return (type, selector, expr, attributes)
示例#2
0
 def parse_xml(cls, el, source_location, attr_name='pyref', 
               default_function=None, default_objs={}):
     """
     Parse an instance of this object from the attributes in the
     given element.
     """
     s = el.get(attr_name)
     args = {}
     for name, value in list(el.attrib.items()):
         if name.startswith('pyarg-'):
             args[name[len('pyarg-'):]] = value
     if not s:
         if args:
             raise DeliveranceSyntaxError(
                 "You provided pyargs-* attributes (%s) but no %s attribute"
                 % (cls._format_args(args), attr_name),
                 element=el, source_location=source_location)
         return None
     s = s.strip()
     module = filename = None
     if s.startswith('file:'):
         filename = s[len('file:'):]
         if ':' in filename:
             filename, func = filename.split(':', 1)
         else:
             func = default_function
     else:
         # A module name
         if ':' in s:
             module, func = s.split(':', 1)
         else:
             module = s
     if func is None:
         raise DeliveranceSyntaxError(
             "You must provide a function name",
             element=s, source_location=source_location)
     if filename:
         full_file = cls.expand_filename(filename, source_location)
         if not os.path.exists(full_file):
             if full_file != filename:
                 raise DeliveranceSyntaxError(
                     "The filename %r (expanded from %r) does not exist"
                     % (full_file, filename),
                     element=s, source_location=source_location)
             else:
                 raise DeliveranceSyntaxError(
                     "The filename %r does not exist" % full_file,
                     element=s, source_location=source_location)
     return cls(module_name=module, filename=filename, function_name=func, 
                args=args, attr_name=attr_name, default_objs=default_objs,
                source_location=source_location)
示例#3
0
 def parse_xml(cls, el, source_location):
     """ Parse an instance from an etree XML element """
     app = el.get('app')
     if not app:
         raise DeliveranceSyntaxError(
             "A ``<wsgi>`` tag must have an ``app`` attribute",
             element=el, source_location=source_location)
     return cls(app, source_location=source_location)
示例#4
0
 def parse_xml(cls, el, source_location):
     """
     Parses the <match> element into a match object
     """
     matchargs = cls.parse_match_xml(el, source_location)
     assert el.tag == cls.element_name
     classes = el.get('class', '').split()
     abort = asbool(el.get('abort'))
     if not abort and not classes:
         ## FIXME: source location
         raise DeliveranceSyntaxError(
             "You must provide some classes in the class attribute")
     if abort and classes:
         ## FIXME: source location
         raise DeliveranceSyntaxError(
             'You cannot provide both abort="1" and class="%s"' %
             (' '.join(classes)))
     last = asbool(el.get('last'))
     return cls(classes=classes, abort=abort, last=last, **matchargs)
示例#5
0
 def parse_xml(cls, el, source_location):
     """Parse an instance from an etree XML element"""
     assert el.tag == 'theme'
     href = el.get('href')
     pyref = PyReference.parse_xml(el, source_location, default_function='get_theme',
                                   default_objs=dict(AbortTheme=AbortTheme))
     if not pyref and not href:
         ## FIXME: also warn when pyref and href?
         raise DeliveranceSyntaxError(
             'You must provide at least one of href, pymodule, or the '
             'pyfile attribute', element=el)
     return cls(href=href, pyref=pyref,
                source_location=source_location)
示例#6
0
 def parse_xml(cls, el, source_location):
     """Parse an instance from an etree XML element"""
     href = el.get('href')
     pyref = PyReference.parse_xml(
         el, source_location, 
         default_function='get_proxy_dest', default_objs=dict(AbortProxy=AbortProxy))
     next = asbool(el.get('next'))
     if next and (href or pyref):
         raise DeliveranceSyntaxError(
             'If you have a next="1" attribute you cannot also have an href '
             'or pyref attribute',
             element=el, source_location=source_location)
     return cls(href, pyref, next=next, source_location=source_location)
示例#7
0
 def parse_xml(cls, el, source_location):
     """Parse an instance from an etree XML element"""
     assert el.tag == 'request'
     pyref = PyReference.parse_xml(el,
                                   source_location,
                                   default_function='modify_proxy_request',
                                   default_objs=dict(AbortProxy=AbortProxy))
     header = el.get('header')
     content = el.get('content')
     ## FIXME: the misspelling is annoying :(
     if (not header and content) or (not content and header):
         raise DeliveranceSyntaxError(
             "If you provide a header attribute you must provide a "
             "content attribute, and vice versa",
             element=el,
             source_location=source_location)
     return cls(pyref, header, content, source_location=source_location)
示例#8
0
 def parse_xml(cls, el, source_location):
     """Create an instance from a parsed element"""
     assert el.tag == 'response'
     pyref = PyReference.parse_xml(
         el, source_location,
         default_function='modify_proxy_response', 
         default_objs=dict(AbortProxy=AbortProxy))
     header = el.get('header')
     content = el.get('content')
     if (not header and content) or (not content and header):
         raise DeliveranceSyntaxError(
             "If you provide a header attribute you must provide a content "
             "attribute, and vice versa",
             element=el, source_location=source_location)
     rewrite_links = asbool(el.get('rewrite-links'))
     return cls(pyref=pyref, header=header, content=content, 
                rewrite_links=rewrite_links, source_location=source_location)
示例#9
0
 def expand_filename(filename, source_location=None):
     """
     Expand environmental variables in a filename
     """
     if source_location and source_location.startswith('file:'):
         here = os.path.dirname(filetourl.url_to_filename(source_location))
     else:
         ## FIXME: this is a lousy default:
         here = ''
     vars = NestedDict(dict(here=here), DefaultDict(os.environ))
     tmpl = Template(filename)
     try:
         return tmpl.substitute(vars)
     except ValueError as e:
         raise DeliveranceSyntaxError(
             "The filename %r contains bad $ substitutions: %s"
             % (filename, e),
             filename, source_location=source_location)
示例#10
0
 def parse_xml(cls, doc, source_location):
     """
     Parses the given XML/etree document into an instance of this
     class.
     """
     assert doc.tag == 'ruleset'
     matchers = []
     clientsides = []
     rules = []
     default_theme = None
     for el in doc.iterchildren():
         if el.tag == 'match':
             matcher = Match.parse_xml(el, source_location)
             matchers.append(matcher)
         elif el.tag == 'clientside':
             matcher = ClientsideMatch.parse_xml(el, source_location)
             clientsides.append(matcher)
         elif el.tag == 'rule':
             rule = Rule.parse_xml(el, source_location)
             rules.append(rule)
         elif el.tag == 'theme':
             ## FIXME: Add parse error
             default_theme = Theme.parse_xml(el, source_location)
         elif el.tag in ('proxy', 'server-settings', Comment):
             # Handled elsewhere, so we just ignore this element
             continue
         else:
             ## FIXME: source location?
             raise DeliveranceSyntaxError(
                 "Invalid tag %s (unknown tag name %r)" %
                 (tostring(el).split('>', 1)[0] + '>', el.tag),
                 element=el)
     rules_by_class = {}
     for rule in rules:
         for class_name in rule.classes:
             rules_by_class.setdefault(class_name, []).append(rule)
     return cls(matchers,
                clientsides,
                rules_by_class,
                default_theme=default_theme,
                source_location=source_location)
示例#11
0
    def parse_xml(cls, el, source_location, environ=None, traverse=False):
        """Parse an instance from an etree XML element"""

        middleware_factory = None
        middleware_factory_kwargs = None

        if traverse and el.tag != 'server-settings':
            try:
                el = el.xpath('//server-settings')[0]
            except IndexError:
                raise DeliveranceSyntaxError(
                    "There is no <server-settings> element", element=el)
        if environ is None:
            environ = os.environ
        assert el.tag == 'server-settings'
        server_host = 'localhost:8080'
        ## FIXME: should these defaults be passed in:
        execute_pyref = True
        display_local_files = True
        edit_local_files = True
        dev_allow_ips = []
        dev_deny_ips = []
        dev_htpasswd = None
        dev_expiration = 0
        dev_users = {}
        dev_secret_file = os.path.join(tempfile.gettempdir(), 'deliverance',
                                       'devauth.txt')
        for child in el:
            if child.tag is Comment:
                continue
            ## FIXME: should some of these be attributes?
            elif child.tag == 'server':
                server_host = cls.substitute(child.text, environ)
            elif child.tag == 'execute-pyref':
                execute_pyref = asbool(cls.substitute(child.text, environ))
            elif child.tag == 'dev-allow':
                dev_allow_ips.extend(
                    cls.substitute(child.text, environ).split())
            elif child.tag == 'dev-deny':
                dev_deny_ips.extend(
                    cls.substitute(child.text, environ).split())
            elif child.tag == 'dev-htpasswd':
                dev_htpasswd = os.path.join(
                    os.path.dirname(url_to_filename(source_location)),
                    cls.substitute(child.text, environ))
            elif child.tag == 'dev-expiration':
                dev_expiration = cls.substitute(child.text, environ)
                if dev_expiration:
                    dev_expiration = int(dev_expiration)
            elif child.tag == 'display-local-files':
                display_local_files = asbool(
                    cls.substitute(child.text, environ))
            elif child.tag == 'edit-local-files':
                edit_local_files = asbool(cls.substitute(child.text, environ))
            elif child.tag == 'dev-user':
                username = cls.substitute(child.get('username', ''), environ)
                ## FIXME: allow hashed password?
                password = cls.substitute(child.get('password', ''), environ)
                if not username or not password:
                    raise DeliveranceSyntaxError(
                        "<dev-user> must have both a username and password attribute",
                        element=child)
                if username in dev_users:
                    raise DeliveranceSyntaxError(
                        '<dev-user username="******"> appears more than once' %
                        username,
                        element=el)
                dev_users[username] = password
            elif child.tag == 'dev-secret-file':
                dev_secret_file = cls.substitute(child.text, environ)
            elif child.tag == 'middleware-factory':
                ref = PyReference.parse_xml(child, source_location)
                middleware_factory = ref.function
                middleware_factory_kwargs = ref.args or None
            else:
                raise DeliveranceSyntaxError(
                    'Unknown element in <server-settings>: <%s>' % child.tag,
                    element=child)
        if dev_users and dev_htpasswd:
            raise DeliveranceSyntaxError(
                "You can use <dev-htpasswd> or <dev-user>, but not both",
                element=el)
        if not dev_users and not dev_htpasswd:
            ## FIXME: not sure this is the best way to warn
            print(
                'Warning: no <dev-users> or <dev-htpasswd>; logging is inaccessible'
            )
        ## FIXME: add a default allow_ips of 127.0.0.1?
        return cls(server_host,
                   execute_pyref=execute_pyref,
                   display_local_files=display_local_files,
                   edit_local_files=edit_local_files,
                   dev_allow_ips=dev_allow_ips,
                   dev_deny_ips=dev_deny_ips,
                   dev_users=dev_users,
                   dev_htpasswd=dev_htpasswd,
                   dev_expiration=dev_expiration,
                   source_location=source_location,
                   dev_secret_file=dev_secret_file,
                   middleware_factory=middleware_factory,
                   middleware_factory_kwargs=middleware_factory_kwargs)
示例#12
0
    def parse_xml(cls, el, source_location):
        """Parse this document from an XML/etree element"""
        assert el.tag == 'proxy'
        match = ProxyMatch.parse_xml(el, source_location)
        dest = None
        wsgi = None
        request_modifications = []
        response_modifications = []
        strip_script_name = True
        keep_host = False
        editable = asbool(el.get('editable'))
        rewriting_links = None

        ## FIXME: this inline validation is a bit brittle because it is
        ##        order-dependent, but validation errors generally aren't
        for child in el:
            if child.tag == 'dest':
                if dest is not None:
                    raise DeliveranceSyntaxError(
                        "You cannot have more than one <dest> tag (second tag: %s)"
                        % xml_tostring(child),
                        element=child,
                        source_location=source_location)
                if wsgi is not None:
                    raise DeliveranceSyntaxError(
                        "You cannot have both a <dest> tag and a <wsgi> tag (second tag: %s)"
                        % xml_tostring(child),
                        element=child,
                        source_location=source_location)
                dest = ProxyDest.parse_xml(child, source_location)
            elif child.tag == 'wsgi':
                if wsgi is not None:
                    raise DeliveranceSyntaxError(
                        "You cannot have more than one <wsgi> tag (second tag: %s)"
                        % xml_tostring(child),
                        element=child,
                        source_location=source_location)
                if dest is not None:
                    raise DeliveranceSyntaxError(
                        "You cannot have both a <dest> tag and a <wsgi> tag (second tag: %s)"
                        % xml_tostring(child),
                        element=child,
                        source_location=source_location)
                if rewriting_links is not None:
                    raise DeliveranceSyntaxError(
                        "You cannot use ``<response rewrite-links='1'>`` in a proxy with a ``<wsgi>`` tag",
                        element=child,
                        source_location=source_location)

                wsgi = ProxyWsgi.parse_xml(child, source_location)

            elif child.tag == 'transform':
                if child.get('strip-script-name'):
                    strip_script_name = asbool(child.get('strip-script-name'))
                if child.get('keep-host'):
                    keep_host = asbool(child.get('keep-host'))
                ## FIXME: error on other attrs
            elif child.tag == 'request':
                request_modifications.append(
                    ProxyRequestModification.parse_xml(child, source_location))
            elif child.tag == 'response':
                mod = ProxyResponseModification.parse_xml(
                    child, source_location)
                if mod.rewrite_links == True:
                    rewriting_links = mod

                if wsgi is not None:
                    raise DeliveranceSyntaxError(
                        "You cannot use ``<response rewrite-links='1'>`` in a proxy with a ``<wsgi>`` tag",
                        element=child,
                        source_location=source_location)

                response_modifications.append(mod)

            elif child.tag is Comment:
                continue
            else:
                raise DeliveranceSyntaxError("Unknown tag in <proxy>: %s" %
                                             xml_tostring(child),
                                             element=child,
                                             source_location=source_location)
        if editable:
            if not dest:
                ## FIXME: should this always be a test?
                raise DeliveranceSyntaxError("You must have a <dest> tag",
                                             element=el,
                                             source_location=source_location)
            try:
                href = uri_template_substitute(
                    dest.href, dict(here=posixpath.dirname(source_location)))
            except KeyError:
                raise DeliveranceSyntaxError(
                    'You can only use <proxy editable="1"> if you have a <dest href="..."> that only contains {here} (you have %s)'
                    % (dest.href))
            if not href.startswith('file:'):
                raise DeliveranceSyntaxError(
                    'You can only use <proxy editable="1"> if you have a <dest href="file:///..."> (you have %s)'
                    % (dest))
        classes = el.get('class', '').split() or None
        inst = cls(match,
                   dest,
                   request_modifications,
                   response_modifications,
                   strip_script_name=strip_script_name,
                   keep_host=keep_host,
                   source_location=source_location,
                   classes=classes,
                   editable=editable,
                   wsgi=wsgi)
        match.proxy = inst
        return inst