def _try_decode(subject, charsets): for charset in charsets[:-1]: try: return tonative(subject, charset) except ValueError: pass return tonative(subject, charsets[-1])
def _try_decode_header(header, charset): global FALLBACK_CHARSET for enc in (charset, FALLBACK_CHARSET): try: return tonative(ntob(tonative(header, 'latin1'), 'latin1'), enc) except ValueError as ve: last_err = ve else: raise last_err
def get_error_page(status, **kwargs): """Return an HTML page, containing a pretty error response. status should be an int or a str. kwargs will be interpolated into the page template. """ import cherrypy try: code, reason, message = _httputil.valid_status(status) except ValueError: raise cherrypy.HTTPError(500, _exc_info()[1].args[0]) # We can't use setdefault here, because some # callers send None for kwarg values. if kwargs.get('status') is None: kwargs['status'] = "%s %s" % (code, reason) if kwargs.get('message') is None: kwargs['message'] = message if kwargs.get('traceback') is None: kwargs['traceback'] = '' if kwargs.get('version') is None: kwargs['version'] = cherrypy.__version__ for k, v in iteritems(kwargs): if v is None: kwargs[k] = "" else: kwargs[k] = tonative(_escape(kwargs[k])) # Use a custom template or callable for the error page? pages = cherrypy.serving.request.error_page error_page = pages.get(code) or pages.get('default') if error_page: try: if hasattr(error_page, '__call__'): return error_page(**kwargs) else: data = open(error_page, 'rb').read() return tonative(data) % kwargs except: e = _format_exception(*_exc_info())[-1] m = kwargs['message'] if m: m += "<br />" m += "In addition, the custom error page failed:\n<br />%s" % e kwargs['message'] = m return _HTTPErrorTemplate % kwargs
def __init__(self, urls, status=None, encoding=None): if isinstance(urls, text_or_bytes): urls = [urls] abs_urls = [] for url in urls: url = tonative(url, encoding or self.encoding) # Note that urljoin will "do the right thing" whether url is: # 1. a complete URL with host (e.g. "http://www.example.com/test") # 2. a URL relative to root (e.g. "/dummy") # 3. a URL relative to the current path # Note that any query string in cherrypy.request is discarded. url = urllib.parse.urljoin(cherrypy.url(), url) abs_urls.append(url) self.urls = abs_urls status = ( int(status) if status is not None else self.default_status ) if not 300 <= status <= 399: raise ValueError('status must be between 300 and 399.') CherryPyException.__init__(self, abs_urls, status)
def __init__(self, urls, status=None, encoding=None): import cherrypy request = cherrypy.serving.request if isinstance(urls, basestring): urls = [urls] abs_urls = [] for url in urls: url = tonative(url, encoding or self.encoding) # Note that urljoin will "do the right thing" whether url is: # 1. a complete URL with host (e.g. "http://www.example.com/test") # 2. a URL relative to root (e.g. "/dummy") # 3. a URL relative to the current path # Note that any query string in cherrypy.request is discarded. url = _urljoin(cherrypy.url(), url) abs_urls.append(url) self.urls = abs_urls # RFC 2616 indicates a 301 response code fits our goal; however, # browser support for 301 is quite messy. Do 302/303 instead. See # http://www.alanflavell.org.uk/www/post-redirect.html if status is None: if request.protocol >= (1, 1): status = 303 else: status = 302 else: status = int(status) if status < 300 or status > 399: raise ValueError("status must be between 300 and 399.") self.status = status CherryPyException.__init__(self, abs_urls, status)
def __init__(self, urls, status=None, encoding=None): request = cherrypy.serving.request if isinstance(urls, text_or_bytes): urls = [urls] abs_urls = [] for url in urls: url = tonative(url, encoding or self.encoding) # Note that urljoin will "do the right thing" whether url is: # 1. a complete URL with host (e.g. "http://www.example.com/test") # 2. a URL relative to root (e.g. "/dummy") # 3. a URL relative to the current path # Note that any query string in cherrypy.request is discarded. url = urllib.parse.urljoin(cherrypy.url(), url) abs_urls.append(url) self.urls = abs_urls # RFC 2616 indicates a 301 response code fits our goal; however, # browser support for 301 is quite messy. Do 302/303 instead. See # http://www.alanflavell.org.uk/www/post-redirect.html if status is None: if request.protocol >= (1, 1): status = 303 else: status = 302 else: status = int(status) if status < 300 or status > 399: raise ValueError('status must be between 300 and 399.') self.status = status CherryPyException.__init__(self, abs_urls, status)
def test_unicode(self): url = ntou("/static/Слава Україні.html", 'utf-8') # quote function requires str url = tonative(url, 'utf-8') url = urllib.parse.quote(url) self.getPage(url) expected = ntou("Героям Слава!", 'utf-8') self.assertInBody(expected)
def _try_decode_map_values(param_map, charset): global FALLBACK_CHARSET for enc in (charset, FALLBACK_CHARSET): try: return {k: tonative(v, enc) for k, v in param_map.items()} except ValueError as ve: last_err = ve else: raise last_err
def test_unicode(self): ensure_unicode_filesystem() self.unicode_file() url = ntou('/static/Слава Україні.html', 'utf-8') # quote function requires str url = tonative(url, 'utf-8') url = urllib.parse.quote(url) self.getPage(url) expected = ntou('Героям Слава!', 'utf-8') self.assertInBody(expected)
def test_unicode(self): ensure_unicode_filesystem() with self.unicode_file(): url = ntou('/static/Слава Україні.html', 'utf-8') # quote function requires str url = tonative(url, 'utf-8') url = urllib.parse.quote(url) self.getPage(url) expected = ntou('Героям Слава!', 'utf-8') self.assertInBody(expected)
def error_handler(status, message, traceback, version): _ = traceback, version log.warning("Engine error: %s: %s", status, message) response = cherrypy.serving.response response.status = 303 response.headers["Content-Type"] = "text/html;charset=utf-8" response.headers["Location"] = urllib.parse.urljoin( cherrypy.url(), tonative( cherrypy.config["engine.settings"]["endpoints"]["access_denied"], "utf-8" ) ) data = "This resource can be found at <a href=%s>%s</a>." % ( saxutils.quoteattr(cherrypy.response.headers["Location"]), html.escape(cherrypy.response.headers["Location"], quote=False) ) response.headers.pop("Content-Length", None) return data
def __init__(self, urls, status=None, encoding=None): import cherrypy request = cherrypy.serving.request if isinstance(urls, text_or_bytes): urls = [urls] self.urls = [tonative(url, encoding or self.encoding) for url in urls] # RFC 2616 indicates a 301 response code fits our goal; however, # browser support for 301 is quite messy. Do 302/303 instead. See # http://www.alanflavell.org.uk/www/post-redirect.html if status is None: if request.protocol >= (1, 1): status = 303 else: status = 302 else: status = int(status) if status < 300 or status > 399: raise ValueError('status must be between 300 and 399.') self.status = status CherryPyException.__init__(self, self.urls, status)
def upload(self): if not cherrypy.request.method == 'POST': raise AssertionError("'POST' != request.method %r" % cherrypy.request.method) return "thanks for '%s'" % tonative(cherrypy.request.body.read())
def _get_response_status(resp): try: response_status = resp.output_status except AttributeError: response_status = resp.status return tonative(response_status, 'utf-8')
def on_start_resource(self, **kwargs): """ Checks if the current request requires authentication if it requires authentication it will look for the token if the token doesn't exist, it will raise a redirect error landing on the authentication page. """ default_args = { "required": False, "login": False } kwargs.update(default_args) if kwargs['required'] is False and kwargs['login'] is False: # Authoirzation not required to access this url return token = None try: if 'Authorization' in cherrypy.request.headers: token = cherrypy.request.headers['Authorization'] elif 'Authorization' in cherrypy.request.cookie: token = cherrypy.request.cookie['Authorization'] except: logging.exception("Couldn't acquire a token") if token is not None and token.startswith("Bearer"): logging.debug("Attempting token authorisation") try: self.auth_mech.verifyToken(token) except AuthenticationFailure as e: raise cherrypy.HTTPRedirect(self.auth_url) except RenewToken as e: # Set the new token on the response headers cherrypy.response.headers['Authorization'] = 'Bearer ' + e.token.decode("utf-8") cherrypy.response.cookie['Authorization'] = 'Bearer ' + e.token.decode("utf-8") return # Get the username/password from URL (authorization basic) accept_charset='utf-8' fallback_charset = 'ISO-8859-1' if kwargs['login'] is True and token is not None and token.startswith("Basic"): # split() error, base64.decodestring() error msg = 'Bad Request' with cherrypy.HTTPError.handle((ValueError, binascii.Error), 400, msg): scheme, params = token.split(' ', 1) charsets = accept_charset, fallback_charset decoded_params = base64.b64decode(params.encode('ascii')) decoded_params = _try_decode(decoded_params, charsets) decoded_params = ntou(decoded_params) decoded_params = unicodedata.normalize('NFC', decoded_params) decoded_params = tonative(decoded_params) username, password = decoded_params.split(':', 1) if self.auth_mech.checkpass(username, password): token = self.auth_mech.generateToken(username) cherrypy.request.login = username cherrypy.response.headers['Authorization'] = 'Bearer ' + token.decode("utf-8") cherrypy.response.cookie['Authorization'] = 'Bearer ' + token.decode("utf-8") return # successful authentication if self.auth_url is not None: raise cherrypy.HTTPRedirect(self.auth_url) # # Check if the request was a JSON/API request or not # # if cherrypy.request.headers['Accept'] != 'application/json': # If we're a browser set the WWW-Authenticate header charset = accept_charset.upper() charset_declaration = ( ('charset="%s"' % charset) if charset != fallback_charset else '' ) cherrypy.response.headers['www-authenticate'] = ('Basic %s' % (charset)) raise cherrypy.HTTPError(401, 'You are not authorized to access this resource')
def get_error_page(status, errors=None, **kwargs): """Return an HTML page, containing a pretty error response. status should be an int or a str. kwargs will be interpolated into the page template. """ try: code, reason, message = _httputil.valid_status(status) except ValueError: raise cherrypy.HTTPError(500, _exc_info()[1].args[0]) # We can't use setdefault here, because some # callers send None for kwarg values. if kwargs.get('status') is None: kwargs['status'] = '%s %s' % (code, reason) if kwargs.get('message') is None: kwargs['message'] = message if kwargs.get('traceback') is None: kwargs['traceback'] = '' if kwargs.get('version') is None: kwargs['version'] = cherrypy.__version__ for k, v in kwargs.items(): if v is None: kwargs[k] = '' else: kwargs[k] = _escape(kwargs[k]) # Use a custom template or callable for the error page? pages = cherrypy.serving.request.error_page error_page = pages.get(code) or pages.get('default') # Default template, can be overridden below. template = _HTTPErrorTemplate if error_page: try: if hasattr(error_page, '__call__'): # The caller function may be setting headers manually, # so we delegate to it completely. We may be returning # an iterator as well as a string here. # # We *must* make sure any content is not unicode. result = error_page(errors=errors, **kwargs) if cherrypy.lib.is_iterator(result): return UTF8StreamEncoder(result) elif isinstance(result, str): # str is OK for Python3 return result.encode('utf-8') else: if not isinstance(result, bytes): raise ValueError( 'error page function did not ' 'return a bytestring, unicodestring or an ' 'iterator - returned object of type {}.' .format(type(result).__name__)) return result else: # Load the template from this path. template = tonative(open(error_page, 'rb').read()) except: e = _format_exception(*_exc_info())[-1] m = kwargs['message'] if m: m += '<br />' m += 'In addition, the custom error page failed:\n<br />%s' % e kwargs['message'] = m response = cherrypy.serving.response response.headers['Content-Type'] = 'text/html;charset=utf-8' result = template % kwargs return result.encode('utf-8')
def get_error_page(status, **kwargs): """Return an HTML page, containing a pretty error response. status should be an int or a str. kwargs will be interpolated into the page template. """ try: code, reason, message = _httputil.valid_status(status) except ValueError: raise cherrypy.HTTPError(500, _exc_info()[1].args[0]) # We can't use setdefault here, because some # callers send None for kwarg values. if kwargs.get('status') is None: kwargs['status'] = '%s %s' % (code, reason) if kwargs.get('message') is None: kwargs['message'] = message if kwargs.get('traceback') is None: kwargs['traceback'] = '' if kwargs.get('version') is None: kwargs['version'] = cherrypy.__version__ for k, v in six.iteritems(kwargs): if v is None: kwargs[k] = '' else: kwargs[k] = escape_html(kwargs[k]) # Use a custom template or callable for the error page? pages = cherrypy.serving.request.error_page error_page = pages.get(code) or pages.get('default') # Default template, can be overridden below. template = _HTTPErrorTemplate if error_page: try: if hasattr(error_page, '__call__'): # The caller function may be setting headers manually, # so we delegate to it completely. We may be returning # an iterator as well as a string here. # # We *must* make sure any content is not unicode. result = error_page(**kwargs) if cherrypy.lib.is_iterator(result): from cherrypy.lib.encoding import UTF8StreamEncoder return UTF8StreamEncoder(result) elif isinstance(result, six.text_type): return result.encode('utf-8') else: if not isinstance(result, bytes): raise ValueError( 'error page function did not ' 'return a bytestring, six.text_type or an ' 'iterator - returned object of type %s.' % (type(result).__name__)) return result else: # Load the template from this path. template = tonative(open(error_page, 'rb').read()) except Exception: e = _format_exception(*_exc_info())[-1] m = kwargs['message'] if m: m += '<br />' m += 'In addition, the custom error page failed:\n<br />%s' % e kwargs['message'] = m response = cherrypy.serving.response response.headers['Content-Type'] = 'text/html;charset=utf-8' result = template % kwargs return result.encode('utf-8')
def basic_auth(realm, checkpassword, debug=False, accept_charset='utf-8'): """A CherryPy tool which hooks at before_handler to perform HTTP Basic Access Authentication, as specified in :rfc:`2617` and :rfc:`7617`. If the request has an 'authorization' header with a 'Basic' scheme, this tool attempts to authenticate the credentials supplied in that header. If the request has no 'authorization' header, or if it does but the scheme is not 'Basic', or if authentication fails, the tool sends a 401 response with a 'WWW-Authenticate' Basic header. realm A string containing the authentication realm. checkpassword A callable which checks the authentication credentials. Its signature is checkpassword(realm, username, password). where username and password are the values obtained from the request's 'authorization' header. If authentication succeeds, checkpassword returns True, else it returns False. """ fallback_charset = 'ISO-8859-1' if '"' in realm: raise ValueError('Realm cannot contain the " (quote) character.') request = cherrypy.serving.request auth_header = request.headers.get('authorization') if auth_header is not None: # split() error, base64.decodestring() error msg = 'Bad Request' with cherrypy.HTTPError.handle((ValueError, binascii.Error), 400, msg): scheme, params = auth_header.split(' ', 1) if scheme.lower() == 'basic': decoded_params = base64_decode(params) decoded_params = ntob(decoded_params) last_err = None for charset in (accept_charset, fallback_charset): try: decoded_params = tonative(decoded_params, charset) break except ValueError as ve: last_err = ve else: raise last_err decoded_params = ntou(decoded_params) decoded_params = unicodedata.normalize('NFC', decoded_params) decoded_params = tonative(decoded_params) username, password = decoded_params.split(':', 1) if checkpassword(realm, username, password): if debug: cherrypy.log('Auth succeeded', 'TOOLS.AUTH_BASIC') request.login = username return # successful authentication charset = accept_charset.upper() charset_declaration = ((', charset="%s"' % charset) if charset != fallback_charset else '') # Respond with 401 status and a WWW-Authenticate header cherrypy.serving.response.headers['www-authenticate'] = ( 'Basic realm="%s"%s' % (realm, charset_declaration)) raise cherrypy.HTTPError(401, 'You are not authorized to access that resource')