Example #1
0
 def keep_status_start_response(status, headers, exc_info=None):
     for header, value in headers:
         if header.lower() == "set-cookie":
             self.headers.append((header, value))
         else:
             replace_header(self.headers, header, value)
     return start_response(self.status, self.headers, exc_info)
Example #2
0
    def add_metadata(self, environ, identity):
        request = Request(environ)
        log.debug("CSRFMetadataProvider.add_metadata(%s)" % request.path)

        session_id = environ.get(self.auth_session_id)
        if not session_id:
            session_id = request.cookies.get(self.session_cookie)
        log.debug('session_id = %r' % session_id)

        if session_id and session_id != 'Set-Cookie:':
            token = sha1(session_id).hexdigest()
            identity.update({self.csrf_token_id: token})
            log.debug("Identity updated with CSRF token")
            path = self.strip_script(environ, request.path)
            if path == self.login_handler:
                log.debug('Setting CSRF_AUTH_STATE')
                environ[self.auth_state] = True
                environ[self.token_env] = token
            else:
                environ[self.token_env] = self.extract_csrf_token(request)

            app = environ.get('repoze.who.application')
            if app:
                # This occurs during login in some application configurations
                if isinstance(app, HTTPFound) and environ.get(self.auth_state):
                    log.debug('Got HTTPFound(302) from repoze.who.application')
                    loc = update_qs(app.location(), {self.csrf_token_id: str(token)})

                    replace_header(app.headers, 'location', loc)
                    log.debug('Altered headers: %s' % str(app.headers))
        else:
            log.warning("Invalid session cookie %r, not setting CSRF token!" %session_id)
 def keep_status_start_response(status, headers, exc_info=None):
     for header, value in headers:
         if header.lower() == 'set-cookie':
             self.headers.append((header, value))
         else:
             replace_header(self.headers, header, value)
     return start_response(self.status, self.headers, exc_info)
Example #4
0
 def request(self, uri, method="GET", body=None, headers=None,
             wsgi_request=None,
             input=None, output=None, trusted=False):
     method = method.upper()
     wsgi_request = self._coerce_wsgi_request(wsgi_request)
     headers = self._coerce_headers(headers)
     if isinstance(output, basestring) and output.startswith('name '):
         output = get_format(output[5:].strip())
     input, body, headers = self._coerce_input(
         input, body, headers)
     if body and not header_value(headers, 'content-type'):
         # We have to add a content type...
         content_type = input.choose_mimetype(headers, body)
         replace_header(headers, 'content-type', content_type)
     headers = self._set_accept(headers, output)
     if wsgi_request is not None:
         uri = self._resolve_uri(uri, wsgi_request)
         if self._internally_resolvable(uri, wsgi_request):
             return self._internal_request(
                 uri, method=method, body=body, headers=headers,
                 wsgi_request=wsgi_request,
                 input=input, output=output, trusted=trusted)
     else:
         if not scheme_re.search(uri):
             raise ValueError(
                 'You gave a non-absolute URI (%r) and no wsgi_request to '
                 'normalize it against' % uri)
     return self._external_request(
         uri, method=method, body=body, headers=headers,
         wsgi_request=wsgi_request,
         input=input, output=output, trusted=trusted)
Example #5
0
 def filter(self, environ, headers, data):
     status = environ.pop('flexfilter.status')
     if status == HTTPTemporaryRedirect().status:
         response = '''
                     <error>
                       <code>%s</code>
                       <location>%s</location>
                       <message>%s</message>
                     </error>
                     ''' % (status, header_value(headers, 'Location'),
                            header_value(headers, 'Warning'))
         replace_header(headers, 'Content-Length', len(response))
     else:
         root = etree.HTML(data)
         message = escape(etree.tostring(root.find('.//body'),
                                         method="text").strip())
         if not message:
             message = root.find('.//title').text
         details = ""
         code_node = root.find('.//code')
         if code_node  is not None and code_node.text  is not None:
             details = escape(code_node.text)
             # Shorten a bit.
             pos = details.find(',')
             if pos != -1:
                 details = details[:pos]
         response = '''
                     <error>
                       <code>%s</code>
                       <message>%s</message>
                       <details>%s</details>
                     </error>
                     ''' % (status, message, details)
         replace_header(headers, 'Content-Length', len(response))
     return response
Example #6
0
    def add_metadata(self, environ, identity):
        request = Request(environ)
        log.debug("CSRFMetadataProvider.add_metadata(%s)" % request.path)

        session_id = environ.get(self.auth_session_id)
        if not session_id:
            session_id = request.cookies.get(self.session_cookie)
        log.debug('session_id = %r' % session_id)

        if session_id and session_id != 'Set-Cookie:':
            token = sha1(session_id).hexdigest()
            identity.update({self.csrf_token_id: token})
            log.debug("Identity updated with CSRF token")
            path = self.strip_script(environ, request.path)
            if path == self.login_handler:
                log.debug('Setting CSRF_AUTH_STATE')
                environ[self.auth_state] = True
                environ[self.token_env] = token
            else:
                environ[self.token_env] = self.extract_csrf_token(request)

            app = environ.get('repoze.who.application')
            if app:
                # This occurs during login in some application configurations
                if isinstance(app, HTTPFound) and environ.get(self.auth_state):
                    log.debug('Got HTTPFound(302) from repoze.who.application')
                    loc = update_qs(app.location(),
                                    {self.csrf_token_id: str(token)})

                    replace_header(app.headers, 'location', loc)
                    log.debug('Altered headers: %s' % str(app.headers))
        else:
            log.warning("Invalid session cookie %r, not setting CSRF token!" %
                        session_id)
Example #7
0
 def start_response_wrapper(status, headers, exc_info=None):
     location_header = 'location'
     status_code = int(status.split(None,1)[0])
     if (status_code >= 301 and status_code <= 303) or status_code == 307:
         location = header_value(headers, location_header)
         if location:
             replace_header(headers, location_header, resolve_relative_url(location, environ))
     return start_response(status, headers, exc_info)
Example #8
0
def replace_app_header(app, header_name, value):
        from paste.response import replace_header
        if app.headers:
            headers = list(app.headers)
        else:
            headers = []

        replace_header(headers, header_name, value)
        app.headers = headers
Example #9
0
def replace_app_header(app, header_name, value):
        from paste.response import replace_header
        if app.headers:
            headers = list(app.headers)
        else:
            headers = []

        replace_header(headers, header_name, value)
        app.headers = headers
Example #10
0
 def _set_accept(self, headers, output):
     if not output:
         # We apparently don't care what we get
         return headers
     if isinstance(output, Format):
         accept = output.content_types
     elif isinstance(output, basestring):
         # Can't be a name, we already resolved that already
         accept = find_accept_for_type(output)
     else:
         raise TypeError(
             "output should be a mimetype or Format object, not %r"
             % output)
     replace_header(headers, 'Accept', ', '.join(accept))
     return headers
Example #11
0
    def add_metadata(self, environ, identity):
        request = Request(environ)
        log.debug(
            b_('CSRFMetadataProvider.add_metadata(%(r_path)s)') %
            {'r_path': to_bytes(request.path)})

        session_id = environ.get(self.auth_session_id)
        if not session_id:
            session_id = request.cookies.get(self.session_cookie)
        log.debug(b_('session_id = %(s_id)r') % {'s_id': to_bytes(session_id)})

        if session_id and session_id != 'Set-Cookie:':
            environ[self.auth_session_id] = session_id
            token = sha1(session_id).hexdigest()
            identity.update({self.csrf_token_id: token})
            log.debug(b_('Identity updated with CSRF token'))
            path = self.strip_script(environ, request.path)
            if path == self.login_handler:
                log.debug(b_('Setting CSRF_AUTH_STATE'))
                environ[self.auth_state] = True
                environ[self.token_env] = token
            else:
                environ[self.token_env] = self.extract_csrf_token(request)

            app = environ.get('repoze.who.application')
            if app:
                # This occurs during login in some application configurations
                if isinstance(app, HTTPFound) and environ.get(self.auth_state):
                    log.debug(
                        b_('Got HTTPFound(302) from'
                           ' repoze.who.application'))
                    # What possessed people to make this a string or
                    # a function?
                    location = app.location
                    if hasattr(location, '__call__'):
                        location = location()
                    loc = update_qs(location, {self.csrf_token_id: str(token)})

                    headers = app.headers.items()
                    replace_header(headers, 'location', loc)
                    app.headers = ResponseHeaders(headers)
                    log.debug(
                        b_('Altered headers: %(headers)s') %
                        {'headers': to_bytes(app.headers)})
        else:
            log.warning(
                b_('Invalid session cookie %(s_id)r, not setting CSRF'
                   ' token!') % {'s_id': to_bytes(session_id)})
Example #12
0
    def add_metadata(self, environ, identity):
        request = Request(environ)
        log.debug('CSRFMetadataProvider.add_metadata(%(r_path)s)'
                  % {'r_path': to_bytes(request.path)})

        session_id = environ.get(self.auth_session_id)
        if not session_id:
            session_id = request.cookies.get(self.session_cookie)
        log.debug('session_id = %(s_id)r' % {'s_id':
                                             to_bytes(session_id)})

        if session_id and session_id != 'Set-Cookie:':
            environ[self.auth_session_id] = session_id
            token = sha1(session_id).hexdigest()
            identity.update({self.csrf_token_id: token})
            log.debug('Identity updated with CSRF token')
            path = self.strip_script(environ, request.path)
            if path == self.login_handler:
                log.debug('Setting CSRF_AUTH_STATE')
                environ[self.auth_state] = True
                environ[self.token_env] = token
            else:
                environ[self.token_env] = self.extract_csrf_token(request)

            app = environ.get('repoze.who.application')
            if app:
                # This occurs during login in some application configurations
                if isinstance(app, HTTPFound) and environ.get(self.auth_state):
                    log.debug('Got HTTPFound(302) from'
                              ' repoze.who.application')
                    # What possessed people to make this a string or
                    # a function?
                    location = app.location
                    if hasattr(location, '__call__'):
                        location = location()
                    loc = update_qs(location, {self.csrf_token_id:
                                               str(token)})

                    headers = app.headers.items()
                    replace_header(headers, 'location', loc)
                    app.headers = ResponseHeaders(headers)
                    log.debug('Altered headers: %(headers)s' % {
                        'headers': to_bytes(app.headers)})
        else:
            log.warning('Invalid session cookie %(s_id)r, not setting CSRF'
                        ' token!' % {'s_id': to_bytes(session_id)})
Example #13
0
    def __call__(self, environ, start_response):

        path = environ['PATH_INFO']

        if not path:
            path = environ['PATH_INFO'] = '/'

        status, headers, body = intercept_output(environ, self.app,
                                                 self.should_intercept,
                                                 start_response)

        # self.should_intercept returned nada
        if status is None:
            return body

        # don't style if the url should not be styled
        for pattern in self.ignore_paths:
            if pattern.match(path):
                start_response(status, headers)
                return [body]

        # short circuit from theming if this is not likely to be HTML
        content_url = construct_url(environ)
        if self.should_ignore_url(content_url):
            start_response(status, headers)
            return [body]

        # short circuit if we have a 3xx, 204 or 401 error code
        status_code = status.split()[0]
        if status_code.startswith(
                '3') or status_code == '204' or status_code == '401':
            start_response(status, headers)
            return [body]

        # all good - apply the transform
        body = self.apply_transform(environ, body)

        replace_header(headers, 'content-length', str(len(body)))
        replace_header(headers, 'content-type', 'text/html; charset=utf-8')

        start_response(status, headers)
        return [body]
Example #14
0
 def __call__(self, environ, start_response):
     
     path = environ['PATH_INFO']
     
     if not path:
         path = environ['PATH_INFO'] = '/'
     
     status, headers, body = intercept_output(environ, self.app,
                                              self.should_intercept,
                                              start_response)
                                              
     # self.should_intercept returned nada
     if status is None:
         return body
     
     # don't style if the url should not be styled
     for pattern in self.ignore_paths:
         if pattern.match(path):
             start_response(status, headers)
             return [body]
     
     # short circuit from theming if this is not likely to be HTML
     content_url = construct_url(environ)
     if self.should_ignore_url(content_url):
         start_response(status, headers)
         return [body]
         
     # short circuit if we have a 3xx, 204 or 401 error code
     status_code = status.split()[0]
     if status_code.startswith('3') or status_code == '204' or status_code == '401':
         start_response(status, headers)
         return [body]
     
     # all good - apply the transform
     body = self.apply_transform(environ, body)
     
     replace_header(headers, 'content-length', str(len(body)))
     replace_header(headers, 'content-type', 'text/html; charset=utf-8')
     
     start_response(status, headers)
     return [body]
 def prepare_content(self, environ):
     if self.headers:
         headers = list(self.headers)
     else:
         headers = []
     if 'html' in environ.get('HTTP_ACCEPT','') or \
         '*/*' in environ.get('HTTP_ACCEPT',''):
         replace_header(headers, 'content-type', 'text/html')
         content = self.html(environ)
     else:
         replace_header(headers, 'content-type', 'text/plain')
         content = self.plain(environ)
     if isinstance(content, unicode):
         content = content.encode('utf8')
         cur_content_type = (header_value(headers, 'content-type')
                             or 'text/html')
         replace_header(headers, 'content-type',
                        cur_content_type + '; charset=utf8')
     return headers, content
Example #16
0
 def prepare_content(self, environ):
     if self.headers:
         headers = list(self.headers)
     else:
         headers = []
     if 'html' in environ.get('HTTP_ACCEPT','') or \
         '*/*' in environ.get('HTTP_ACCEPT',''):
         replace_header(headers, 'content-type', 'text/html')
         content = self.html(environ)
     else:
         replace_header(headers, 'content-type', 'text/plain')
         content = self.plain(environ)
     if isinstance(content, six.text_type):
         content = content.encode('utf8')
         cur_content_type = (
             header_value(headers, 'content-type')
             or 'text/html')
         replace_header(
             headers, 'content-type',
             cur_content_type + '; charset=utf8')
     return headers, content
Example #17
0
    def __call__(self, environ, start_response):
        """Parse the request body as JSON, look up the method on the
        controller and if it exists, dispatch to it.
        """
        length = 0
        if 'CONTENT_LENGTH' not in environ:
            log.debug("No Content-Length")
            abort(411)
        else:
            if environ['CONTENT_LENGTH'] == '':
                abort(411)
            length = int(environ['CONTENT_LENGTH'])
            log.debug('Content-Length: %s', length)
        if length == 0:
            log.debug("Content-Length is 0")
            abort(411)

        raw_body = environ['wsgi.input'].read(length)
        json_body = json.loads(urllib.unquote_plus(raw_body))

        self._req_id = json_body['id']
        self._req_method = json_body['method']
        self._req_params = json_body['params']
        log.debug('id: %s, method: %s, params: %s', self._req_id,
                  self._req_method, self._req_params)

        self._error = None
        try:
            self._func = self._find_method()
        except AttributeError:
            err = jsonrpc_error(self._req_id, 'method_not_found')
            return err(environ, start_response)

        # now that we have a method, make sure we have enough
        # parameters and pass off control to the controller.
        if not isinstance(self._req_params, dict):
            # JSON-RPC version 1 request.
            arglist = inspect.getargspec(self._func)[0][1:]
            if len(self._req_params) < len(arglist):
                err = jsonrpc_error(self._req_id, 'invalid_params')
                return err(environ, start_response)
            else:
                kargs = dict(zip(arglist, self._req_params))
        else:
            # JSON-RPC version 2 request.  Params may be default, and
            # are already a dict, so skip the parameter length check here.
            kargs = self._req_params

        # XX Fix this namespace clash. One cannot use names below as
        # method argument names as this stands!
        kargs['action'], kargs['environ'] = self._req_method, environ
        kargs['start_response'] = start_response
        self._rpc_args = kargs

        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'application/json')
        start_response(status[0], headers, exc_info[0])

        return output
Example #18
0
    def __call__(self, environ, start_response):
        if not environ.get('transcluder.transclude_response', True):
            return self.app(environ, start_response)
        environ = environ.copy()
        
        environ['transcluder.outcookies'] = {}
        if environ.has_key('HTTP_COOKIE'):
            environ['transcluder.incookies'] = parse_cookie_header(environ['HTTP_COOKIE'])
        else:
            environ['transcluder.incookies'] = []


        if environ.has_key('HTTP_IF_NONE_MATCH'): 
            environ['transcluder.etags'] = parse_merged_etag(environ['HTTP_IF_NONE_MATCH'])
        else: 
            environ['transcluder.etags'] = {}

        request_url = construct_url(environ)
        environ[TRANSCLUDED_HTTP_HEADER] = request_url
        
        variables = self.get_template_vars(request_url)
        
        tc = Transcluder(variables, None,
                         should_include=self.include_predicate,
                         should_recurse=self.recursion_predicate)

        pm = PageManager(request_url, environ, self.deptracker, tc.find_dependencies, self.tasklist, self.etree_subrequest)
        def simple_fetch(url):
            status, headers, body, parsed = pm.fetch(url)
            if status.startswith('200'):
                return parsed
            else:
                raise Exception, 'Status was: %s' % status 
            
        tc.fetch = simple_fetch

        if is_conditional_get(environ) and not pm.is_modified():
            headers = [] 
            pm.merge_headers_into(headers)
            start_response('304 Not Modified', headers)
            return []

        pm.begin_speculative_gets() 

        status, headers, body, parsed = pm.fetch(request_url)

        if parsed is not None: 
            if tc.transclude(parsed, request_url):
                # XXX doctype 
                body = lxmlutils.tostring(parsed, doctype_pair=("-//W3C//DTD HTML 4.01 Transitional//EN",
                                                                "http://www.w3.org/TR/html4/loose.dtd"))
            #else no need to change body at all
            if isinstance(body, unicode):
                body = body.encode('utf-8')
            content_length = str(len(body))
                
            replace_header(headers, 'content-length', content_length)

            replace_header(headers, 'content-type', 'text/html; charset=utf-8')

        pm.merge_headers_into(headers)
        
        start_response(status, headers)
        if isinstance(body, unicode):
            body = body.encode('utf-8')

        return [body]
Example #19
0
        self._rpc_args['environ'] = environ
        self._rpc_args['start_response'] = start_response

        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'application/json')
        start_response(status[0], headers, exc_info[0])
        log.info('IP: %s Request to %s time: %.3fs' % (
            self._get_ip_addr(environ),
            safe_unicode(_get_access_path(environ)), time.time() - start)
        )
        return output

    def _dispatch_call(self):
        """
        Implement dispatch interface specified by WSGIController
        """
        raw_response = ''
        try:
            raw_response = self._inspect_call(self._func)
            if isinstance(raw_response, HTTPError):
Example #20
0
        self._rpc_args["environ"] = environ
        self._rpc_args["start_response"] = start_response

        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(("Content-Length", str(len(output[0]))))
        replace_header(headers, "Content-Type", "application/json")
        start_response(status[0], headers, exc_info[0])

        return output

    def _dispatch_call(self):
        """
        Implement dispatch interface specified by WSGIController
        """
        try:
            raw_response = self._inspect_call(self._func)
            if isinstance(raw_response, HTTPError):
                self._error = str(raw_response)
        except JSONRPCError, e:
            self._error = str(e)
        except Exception, e:
Example #21
0
    def __call__(self, environ, start_response):
        """Parse the request body as JSON, look up the method on the
        controller and if it exists, dispatch to it.
        """
        length = 0
        if 'CONTENT_LENGTH' not in environ:
            log.debug("No Content-Length")
            abort(411)
        else:
            if environ['CONTENT_LENGTH'] == '':
                abort(411)
            length = int(environ['CONTENT_LENGTH'])
            log.debug('Content-Length: %s', length)
        if length == 0:
            log.debug("Content-Length is 0")
            abort(411)

        raw_body = environ['wsgi.input'].read(length)
        json_body = json.loads(urllib.unquote_plus(raw_body))

        self._req_id = json_body['id']
        self._req_method = json_body['method']
        self._req_params = json_body['params']
        log.debug('id: %s, method: %s, params: %s', self._req_id,
                  self._req_method, self._req_params)

        self._error = None
        try:
            self._func = self._find_method()
        except AttributeError:
            err = jsonrpc_error(self._req_id, 'method_not_found')
            return err(environ, start_response)

        # now that we have a method, make sure we have enough
        # parameters and pass off control to the controller.
        if not isinstance(self._req_params, dict):
            # JSON-RPC version 1 request.
            arglist = inspect.getargspec(self._func)[0][1:]
            if len(self._req_params) < len(arglist):
                err = jsonrpc_error(self._req_id, 'invalid_params')
                return err(environ, start_response)
            else:
                kargs = dict(zip(arglist, self._req_params))
        else:
            # JSON-RPC version 2 request.  Params may be default, and
            # are already a dict, so skip the parameter length check here.
            kargs = self._req_params

        # XX Fix this namespace clash. One cannot use names below as
        # method argument names as this stands!
        kargs['action'], kargs['environ'] = self._req_method, environ
        kargs['start_response'] = start_response
        self._rpc_args = kargs

        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'application/json')
        start_response(status[0], headers, exc_info[0])

        return output
Example #22
0
        self._rpc_args['environ'] = environ
        self._rpc_args['start_response'] = start_response

        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'application/json')
        start_response(status[0], headers, exc_info[0])

        return output

    def _dispatch_call(self):
        """
        Implement dispatch interface specified by WSGIController
        """
        try:
            raw_response = self._inspect_call(self._func)
            if isinstance(raw_response, HTTPError):
                self._error = str(raw_response)
        except JSONRPCError, e:
            self._error = str(e)
        except Exception, e:
Example #23
0
class JSONRPCController(WSGIController):
    """
    A WSGI-speaking JSON-RPC 2.0 controller class

    See the specification:
    `<http://groups.google.com/group/json-rpc/web/json-rpc-2-0>`.

    Many parts of this controller are modelled after XMLRPCController
    from Pylons 0.9.7

    Valid controller return values should be json-serializable objects.

    Sub-classes should catch their exceptions and raise JSONRPCError
    if they want to pass meaningful errors to the client. Unhandled
    errors should be caught and return JSONRPC_INTERNAL_ERROR to the
    client.

    Parts of the specification not supported (yet):
     - Notifications
     - Batch
    """

    def _get_method_args(self):
        """Return `self._rpc_args` to dispatched controller method
        chosen by __call__"""
        return self._rpc_args

    def __call__(self, environ, start_response):
        """Parse the request body as JSON, look up the method on the
        controller and if it exists, dispatch to it.
        """
        length = 0
        if 'CONTENT_LENGTH' not in environ:
            log.debug("No Content-Length")
            abort(411)
        else:
            if environ['CONTENT_LENGTH'] == '':
                abort(411)
            length = int(environ['CONTENT_LENGTH'])
            log.debug('Content-Length: %s', length)
        if length == 0:
            log.debug("Content-Length is 0")
            abort(411)

        raw_body = environ['wsgi.input'].read(length)
        json_body = json.loads(urllib.unquote_plus(raw_body))

        self._req_id = json_body['id']
        self._req_method = json_body['method']
        self._req_params = json_body['params']
        log.debug('id: %s, method: %s, params: %s',
                  self._req_id,
                  self._req_method,
                  self._req_params)

        self._error = None
        try:
            self._func = self._find_method()
        except AttributeError, e:
            err = jsonrpc_error(self._req_id, 'method_not_found')
            return err(environ, start_response)

        # now that we have a method, make sure we have enough
        # parameters and pass off control to the controller.
        if not isinstance(self._req_params, dict):
            # JSON-RPC version 1 request.
            arglist = inspect.getargspec(self._func)[0][1:]
            if len(self._req_params) < len(arglist):
                err = jsonrpc_error(self._req_id, 'invalid_params')
                return err(environ, start_response)
            else:
                kargs = dict(zip(arglist, self._req_params))
        else:
            # JSON-RPC version 2 request.  Params may be default, and
            # are already a dict, so skip the parameter length check here.
            kargs = self._req_params

        # XX Fix this namespace clash. One cannot use names below as
        # method argument names as this stands!
        kargs['action'], kargs['environ'] = self._req_method, environ
        kargs['start_response'] = start_response
        self._rpc_args = kargs

        status = []
        headers = []
        exc_info = []
        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'application/json')
        start_response(status[0], headers, exc_info[0])

        return output
Example #24
0
    def __call__(self, environ, start_response):
        """Parse an XMLRPC body for the method, and call it with the
        appropriate arguments"""
        # Pull out the length, return an error if there is no valid
        # length or if the length is larger than the max_body_length.
        log_debug = self._pylons_log_debug
        length = environ.get('CONTENT_LENGTH')
        if length:
            length = int(length)
        else:
            # No valid Content-Length header found
            if log_debug:
                log.debug("No Content-Length found, returning 411 error")
            abort(411)
        if length > self.max_body_length or length == 0:
            if log_debug:
                log.debug(
                    "Content-Length larger than max body length. Max: "
                    "%s, Sent: %s. Returning 413 error", self.max_body_length,
                    length)
            abort(413, "XML body too large")

        body = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
        rpc_args, orig_method = xmlrpclib.loads(body)

        method = self._find_method_name(orig_method)
        func = self._find_method(method)
        if not func:
            if log_debug:
                log.debug("Method: %r not found, returning xmlrpc fault",
                          method)
            return xmlrpc_fault(0, "No such method name %r" % method)(
                environ, start_response)

        # Signature checking for params
        if hasattr(func, 'signature'):
            if log_debug:
                log.debug("Checking XMLRPC argument signature")
            valid_args = False
            params = xmlrpc_sig(rpc_args)
            for sig in func.signature:
                # Next sig if we don't have the same amount of args
                if len(sig) - 1 != len(rpc_args):
                    continue

                # If the params match, we're valid
                if params == sig[1:]:
                    valid_args = True
                    break

            if not valid_args:
                if log_debug:
                    log.debug("Bad argument signature recieved, returning "
                              "xmlrpc fault")
                msg = ("Incorrect argument signature. %r recieved does not "
                       "match %r signature for method %r" % \
                           (params, func.signature, orig_method))
                return xmlrpc_fault(0, msg)(environ, start_response)

        # Change the arg list into a keyword dict based off the arg
        # names in the functions definition
        arglist = inspect.getargspec(func)[0][1:]
        kargs = dict(zip(arglist, rpc_args))
        kargs['action'], kargs['environ'] = method, environ
        kargs['start_response'] = start_response
        self.rpc_kargs = kargs
        self._func = func

        # Now that we know the method is valid, and the args are valid,
        # we can dispatch control to the default WSGIController
        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'text/xml')
        start_response(status[0], headers, exc_info[0])
        return output
Example #25
0
    def __call__(self, environ, start_response):
        """Parse an XMLRPC body for the method, and call it with the
        appropriate arguments"""
        # Pull out the length, return an error if there is no valid
        # length or if the length is larger than the max_body_length.
        log_debug = self._pylons_log_debug
        length = environ.get('CONTENT_LENGTH')
        if length:
            length = int(length)
        else:
            # No valid Content-Length header found
            if log_debug:
                log.debug("No Content-Length found, returning 411 error")
            abort(411)
        if length > self.max_body_length or length == 0:
            if log_debug:
                log.debug("Content-Length larger than max body length. Max: "
                          "%s, Sent: %s. Returning 413 error",
                          self.max_body_length, length)
            abort(413, "XML body too large")

        body = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
        rpc_args, orig_method = xmlrpclib.loads(body)

        method = self._find_method_name(orig_method)
        func = self._find_method(method)
        if not func:
            if log_debug:
                log.debug("Method: %r not found, returning xmlrpc fault",
                          method)
            return xmlrpc_fault(0, "No such method name %r" %
                                method)(environ, start_response)

        # Signature checking for params
        if hasattr(func, 'signature'):
            if log_debug:
                log.debug("Checking XMLRPC argument signature")
            valid_args = False
            params = xmlrpc_sig(rpc_args)
            for sig in func.signature:
                # Next sig if we don't have the same amount of args
                if len(sig)-1 != len(rpc_args):
                    continue

                # If the params match, we're valid
                if params == sig[1:]:
                    valid_args = True
                    break

            if not valid_args:
                if log_debug:
                    log.debug("Bad argument signature recieved, returning "
                              "xmlrpc fault")
                msg = ("Incorrect argument signature. %r recieved does not "
                       "match %r signature for method %r" %
                       (params, func.signature, orig_method))
                return xmlrpc_fault(0, msg)(environ, start_response)

        # Change the arg list into a keyword dict based off the arg
        # names in the functions definition
        arglist = inspect.getargspec(func)[0][1:]
        kargs = dict(zip(arglist, rpc_args))
        kargs['action'], kargs['environ'] = method, environ
        kargs['start_response'] = start_response
        self.rpc_kargs = kargs
        self._func = func

        # Now that we know the method is valid, and the args are valid,
        # we can dispatch control to the default WSGIController
        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)
        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'text/xml')
        start_response(status[0], headers, exc_info[0])
        return output
Example #26
0
 def filter(self, environ, headers, data):
     url = construct_url(environ)
     static_url = environ['olpcproxy.static_url']
     found = environ['olpcproxy.keys']
     action = False
     if self.save_key in found:
         self.store.save_page_set(url, headers, data)
         action = True
     if self.remove_key in found:
         self.store.remove_page(url)
         action = True
     if environ.get('olpcproxy.downloads'):
         for index in environ['olpcproxy.downloads']:
             self.save_download(url, data, index)
         action = True
     if action:
         exc = httpexceptions.HTTPTemporaryRedirect(
             headers=[('Location', url)])
         raise exc
     if '?' not in url:
         url_query = url + '?'
     else:
         url_query = url + '&'
     has_page = self.store.has_url(url)
     page = HTML(data)
     try:
         head = page.xpath('//head')[0]
         body = page.xpath('//body')[0]
     except IndexError:
         # Not a full HTML page
         return data
     self.sub_links(url, page, static_url)
     if has_page:
         time_diff = time.time() - self.store.url_cache_time(url)
         time_diff = format_time_diff(time_diff)
         message = ['This page was cached %s ago.  You may '
                    % time_diff,
                    tag.a('remove it from the cache',
                           href=url_query+self.remove_key)]
         div_class = 'olpc-cached'
     else:
         message = ['This page is NOT cached.  You may ',
                    tag.a('add it to the cache',
                           href=url_query+self.save_key)]
         div_class = None
     if head_style:
         insert_beginning(
             head, tag.style(head_style % {'static_url': static_url},
                              type="text/css"))
     image_location = static_url + '/x-small.gif'
     msg = tag.div(
         message,
         tag.a(tag.img(src=image_location, border=0, id="olpc-close-image"), href="#", onclick="document.getElementById('olpc-top-message').style.display='none'", valign="top"),
         id="olpc-top-message",
         class_=div_class)
     bundles = elquery.get_elements_by_class(body, 'olpc-bundle')
     if bundles:
         image_location = static_url + '/caution.gif'
         append(
             msg,
             tag.br(),
             tag.img(src=image_location),
             "Bundles were found in this page")
         for index, bundle in enumerate(bundles):
             b_msg = tag.div(
                 tag.a(tag.img(src=static_url+'/arrow-down-red.gif', border=0),
                       "You may download this bundle",
                       href=url_query+self.download_key+'='+str(index)))
             insert_beginning(bundle, b_msg)
     insert_beginning(body, msg, tag.br(clear="all"))
     data = tostring(page, True)
     # Now fix up the content-type:
     content_type = header_value(headers, 'content-type') or ''
     content_type = self._charset_re.sub('', content_type).strip().lstrip(';')
     content_type += '; charset=utf'
     replace_header(headers, 'content-type', content_type)
     return data
Example #27
0
    def _handle_request(self, environ, start_response):
        start = time.time()
        ip_addr = self.ip_addr = self._get_ip_addr(environ)
        self._req_id = None
        if 'CONTENT_LENGTH' not in environ:
            log.debug("No Content-Length")
            return jsonrpc_error(retid=self._req_id,
                                 message="No Content-Length in request")
        else:
            length = environ['CONTENT_LENGTH'] or 0
            length = int(environ['CONTENT_LENGTH'])
            log.debug('Content-Length: %s', length)

        if length == 0:
            log.debug("Content-Length is 0")
            return jsonrpc_error(retid=self._req_id,
                                 message="Content-Length is 0")

        raw_body = environ['wsgi.input'].read(length)

        try:
            json_body = json.loads(raw_body)
        except ValueError as e:
            # catch JSON errors Here
            return jsonrpc_error(retid=self._req_id,
                                 message="JSON parse error ERR:%s RAW:%r"
                                 % (e, raw_body))

        # check AUTH based on API key
        try:
            self._req_api_key = json_body['api_key']
            self._req_id = json_body['id']
            self._req_method = json_body['method']
            self._request_params = json_body['args']
            if not isinstance(self._request_params, dict):
                self._request_params = {}

            log.debug(
                'method: %s, params: %s', self._req_method,
                                            self._request_params
            )
        except KeyError as e:
            return jsonrpc_error(retid=self._req_id,
                                 message='Incorrect JSON query missing %s' % e)

        # check if we can find this session using api_key
        try:
            u = User.get_by_api_key(self._req_api_key)
            if u is None:
                return jsonrpc_error(retid=self._req_id,
                                     message='Invalid API key')

            auth_u = AuthUser(dbuser=u)
            if not AuthUser.check_ip_allowed(auth_u, ip_addr):
                return jsonrpc_error(retid=self._req_id,
                        message='request from IP:%s not allowed' % (ip_addr,))
            else:
                log.info('Access for IP:%s allowed', ip_addr)

        except Exception as e:
            return jsonrpc_error(retid=self._req_id,
                                 message='Invalid API key')

        self._error = None
        try:
            self._func = self._find_method()
        except AttributeError as e:
            return jsonrpc_error(retid=self._req_id,
                                 message=str(e))

        # now that we have a method, add self._req_params to
        # self.kargs and dispatch control to WGIController
        argspec = inspect.getargspec(self._func)
        arglist = argspec[0][1:]
        defaults = map(type, argspec[3] or [])
        default_empty = types.NotImplementedType

        # kw arguments required by this method
        func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults),
                                        fillvalue=default_empty))

        # this is little trick to inject logged in user for
        # perms decorators to work they expect the controller class to have
        # authuser attribute set
        self.authuser = auth_u

        # This attribute will need to be first param of a method that uses
        # api_key, which is translated to instance of user at that name
        USER_SESSION_ATTR = 'apiuser'

        if USER_SESSION_ATTR not in arglist:
            return jsonrpc_error(
                retid=self._req_id,
                message='This method [%s] does not support '
                         'authentication (missing %s param)' % (
                                    self._func.__name__, USER_SESSION_ATTR)
            )

        # get our arglist and check if we provided them as args
        for arg, default in func_kwargs.iteritems():
            if arg == USER_SESSION_ATTR:
                # USER_SESSION_ATTR is something translated from API key and
                # this is checked before so we don't need validate it
                continue

            # skip the required param check if it's default value is
            # NotImplementedType (default_empty)
            if default == default_empty and arg not in self._request_params:
                return jsonrpc_error(
                    retid=self._req_id,
                    message=(
                        'Missing non optional `%s` arg in JSON DATA' % arg
                    )
                )

        self._rpc_args = {USER_SESSION_ATTR: u}

        self._rpc_args.update(self._request_params)

        self._rpc_args['action'] = self._req_method
        self._rpc_args['environ'] = environ
        self._rpc_args['start_response'] = start_response

        status = []
        headers = []
        exc_info = []

        def change_content(new_status, new_headers, new_exc_info=None):
            status.append(new_status)
            headers.extend(new_headers)
            exc_info.append(new_exc_info)

        output = WSGIController.__call__(self, environ, change_content)
        output = list(output)
        headers.append(('Content-Length', str(len(output[0]))))
        replace_header(headers, 'Content-Type', 'application/json')
        start_response(status[0], headers, exc_info[0])
        log.info('IP: %s Request to %s time: %.3fs' % (
            self._get_ip_addr(environ),
            safe_unicode(_get_access_path(environ)), time.time() - start)
        )
        return output