BasicAuth Prompt won't show with Status 401 - python

I don't see a password prompt in IE/Firefox even when I send the 401 status code with Tornado:-
import tornado.ioloop
import tornado.web
class UserHandler(tornado.web.RequestHandler):
def get(self, user_id):
self.set_header('WWW-Authenticate', 'Basic realm="users"')
self.send_error(status_code=401)
application = tornado.web.Application([
(r"/users/(\w+)", UserHandler),
],debug=True)
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Moreover the WWW-Authenticate header won't show in the response headers. When I don't send the 401 status, it does show in the header but it doesn't show the password prompt still.

This works although I am not sure why.
import tornado.ioloop
import tornado.web
class UserHandler(tornado.web.RequestHandler):
def get(self, user_id):
self.set_status(401)
self.set_header('WWW-Authenticate', 'Basic realm=Users')
application = tornado.web.Application([
(r"/users/(\w+)", UserHandler),
],debug=True)
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

The solution you found (using set_status() instead of send_error()) works because send_error() calls clear(), which "[r]esets all headers and content", as can be seen in the source. It then calls write_error() to send new content and headers based on the status code.
If you're fine with handling errors by setting all content and headers manually (or with your own methods), just never use send_error().
I found that you can also modify RequestHandler's methods to make send_error() work for you instead of against you. The WWW-Authenticate header is an expected part of any 401 response, so it would be nice if send_error(401) would automatically add it. That can be done by overriding write_error():
class BaseHandler(RequestHandler):
def write_error(self, status_code, **kwargs):
if status_code == 401:
self.add_header(
'WWW-Authenticate',
'Basic realm=' + json.dumps(self.realm)) # adds quotes
super(BaseHandler, self).write_error(status_code, **kwargs)
class UserHandler(BaseHandler):
realm = 'Users'
# ...

Related

How to return 400 (Bad Request) on Flask?

I have created a simple flask app that and I'm reading the response from python as:
response = requests.post(url,data=json.dumps(data), headers=headers )
data = json.loads(response.text)
Now my issue is that under certain conditions I want to return a 400 or 500 message response. So far I'm doing it like this:
abort(400, 'Record not found')
#or
abort(500, 'Some error...')
This does print the message on the terminal:
But in the API response I kept getting a 500 error response:
The structure of the code is as follows:
|--my_app
|--server.py
|--main.py
|--swagger.yml
Where server.py has this code:
from flask import render_template
import connexion
# Create the application instance
app = connexion.App(__name__, specification_dir="./")
# read the swagger.yml file to configure the endpoints
app.add_api("swagger.yml")
# Create a URL route in our application for "/"
#app.route("/")
def home():
"""
This function just responds to the browser URL
localhost:5000/
:return: the rendered template "home.html"
"""
return render_template("home.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port="33")
And main.py has all the function I'm using for the API endpoints.
E.G:
def my_funct():
abort(400, 'Record not found')
When my_funct is called, I get the Record not found printed on the terminal, but not in the response from the API itself, where I always get the 500 message error.
You have a variety of options:
The most basic:
#app.route('/')
def index():
return "Record not found", 400
If you want to access the headers, you can grab the response object:
#app.route('/')
def index():
resp = make_response("Record not found", 400)
resp.headers['X-Something'] = 'A value'
return resp
Or you can make it more explicit, and not just return a number, but return a status code object
from flask_api import status
#app.route('/')
def index():
return "Record not found", status.HTTP_400_BAD_REQUEST
Further reading:
You can read more about the first two here: About Responses (Flask quickstart)
And the third here: Status codes (Flask API Guide)
I like to use the flask.Response class:
from flask import Response
#app.route("/")
def index():
return Response(
"The response body goes here",
status=400,
)
flask.abort is a wrapper around werkzeug.exceptions.abort which is really just a helper method to make it easier to raise HTTP exceptions. That's fine in most cases, but for restful APIs, I think it may be better to be explicit with return responses.
Here's some snippets from a Flask app I wrote years ago. It has an example of a 400 response
import werkzeug
from flask import Flask, Response, json
from flask_restplus import reqparse, Api, Resource, abort
from flask_restful import request
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
api = Api(app)
parser = reqparse.RequestParser()
parser.add_argument('address_to_score', type=werkzeug.datastructures.FileStorage, location='files')
class MissingColumnException(Exception):
pass
class InvalidDateFormatException(Exception):
pass
#api.route('/project')
class Project(Resource):
#api.expect(parser)
#api.response(200, 'Success')
#api.response(400, 'Validation Error')
def post(self):
"""
Takes in an excel file of addresses and outputs a JSON with scores and rankings.
"""
try:
df, input_trees, needed_zones = data.parse_incoming_file(request)
except MissingColumnException as e:
abort(400, 'Excel File Missing Mandatory Column(s):', columns=str(e))
except Exception as e:
abort(400, str(e))
project_trees = data.load_needed_trees(needed_zones, settings['directories']['current_tree_folder'])
df = data.multiprocess_query(df, input_trees, project_trees)
df = data.score_locations(df)
df = data.rank_locations(df)
df = data.replace_null(df)
output_file = df.to_dict('index')
resp = Response(json.dumps(output_file), mimetype='application/json')
resp.status_code = 200
return resp
#api.route('/project/health')
class ProjectHealth(Resource):
#api.response(200, 'Success')
def get(self):
"""
Returns the status of the server if it's still running.
"""
resp = Response(json.dumps('OK'), mimetype='application/json')
resp.status_code = 200
return resp
You can return a tuple with the second element being the status (either 400 or 500).
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return "Record not found", 400
if __name__ == '__main__':
app.run()
Example of calling the API from python:
import requests
response = requests.get('http://127.0.0.1:5000/')
response.text
# 'This is a bad request!'
response.status_code
# 400
I think you're using the abort() function correctly. I suspect the issue here is that an error handler is that is catching the 400 error and then erroring out which causes the 500 error. See here for more info on flask error handling.
As an example, the following would change a 400 into a 500 error:
#app.errorhandler(400)
def handle_400_error(e):
raise Exception("Unhandled Exception")
If you're not doing any error handling, it could be coming from the connexion framework, although I'm not familiar with this framework.
You can simply use #app.errorhandler decorator.
example:
#app.errorhandler(400)
def your_function():
return 'your custom text', 400

How to log redirects in Pyramid?

I'm trying to set it up so that my Pyramid app logs a message whenever it does a redirect - e.g. When one of my views raises HTTPFound.
Creating a custom subclass of HTTPFound worked but it sucks to have to make sure that class is used everywhere in the app.
I then had the idea of creating a custom exception view with context=HTTPFound but that view doesn't seem to get called.
Is there a standard way to set up special handling for a redirect that is global to the app?
To log out redirects you would just have a tween that checks the return status, something like:
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound
def hello1(request):
raise HTTPFound(request.route_url('hello2'))
def hello2(request):
return {'boom': 'shaka'}
def redirect_logger(handler, registry):
def redirect_handler(request):
response = handler(request)
if response.status_int == 302:
print("OMGZ ITS NOT GOING WHERE YOU THINK")
return response
return redirect_handler
def main():
config = Configurator()
config.add_route('hello1', '/', view=hello1)
config.add_route('hello2', '/hi', view=hello2, renderer='json')
config.add_tween('__main__.redirect_logger')
app = config.make_wsgi_app()
return app
if __name__ == '__main__':
app = main()
server = make_server('0.0.0.0', 8080, app)
print("http://0.0.0.0:8080")
server.serve_forever()

Tornado https proxy outputs 405 WARNING

I tried to make proxy server by tornado in Python. The simple http proxy server has worked well, but the https proxy has some problem.
Part of my programs which might have problem are below.
import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from tornado.httpserver import HTTPServer
class HTTPSHandler(RequestHandler):
#tornado.web.asynchronous
def get(self):
print self.request.host, self.request.method
def handle_request(response):
if response.error and not isinstance(response.error, tornado.httpclient.HTTPError):
print "Error:", response.error
else:
self.write(response.body)
self.finish(" ")#in case of response.body == None
request = self.request
req = HTTPRequest(url=request.uri, method=request.method,
headers=request.headers, body=request.body,
allow_nonstandard_methods = True, follow_redirects = False,
validate_cert=True)
http_client = AsyncHTTPClient()
try:
http_client.fetch(req, handle_request)
except Exception as e:
print e
#tornado.web.asynchronous
def post(self):
return self.get()
#tornado.web.asynchronous
def head(self):
return self.get()
#tornado.web.asynchronous
def delete(self):
return self.get()
#tornado.web.asynchronous
def patch(self):
return self.get()
#tornado.web.asynchronous
def put(self):
return self.get()
#tornado.web.asynchronous
def options(self):
return self.get()
if __name__ == "__main__":
app2 = Application([(r"https:.*", HTTPSHandler),])
httpsServer = HTTPServer(app2, ssl_options = {
"certfile": "./server.crt",
"keyfile": "./server.key",
})
app2.listen(444)
tornado.ioloop.IOLoop.instance().start()
It outputs a WARNING like below (when I access to https://www.google.com and https://github.com)
WARNING:tornado.access:405 CONNECT www.google.co.jp:443 (127.0.0.1) 0.69ms
WARNING:tornado.access:405 CONNECT github.com:443 (127.0.0.1) 0.58ms
Finally, web pages which use https protocol could not be displayed with browser error.
ERR_TUNNEL_CONNECTION_FAILED
I guess, this is caused by the tornado’s requestHandler because it does not support CONNECT method.
My question is how can I use the CONNECT method?
I have noticed the way to fix this problem.
At first, SUPPORTED_METHOD should be written in HTTPSHandler class.
This can solve the 405 WARNING and browser error.
class HTTPSHandler(RequestHandler):
SUPPORTED_METHODS = ("CONNECT", "GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS")
#tornado.web.asynchronous
def get(self):
This is written in official document as follows.
If you want to support more methods than the standard GET/HEAD/POST, you
should override the class variable ``SUPPORTED_METHODS`` in your
`RequestHandler` subclass.
Moreover, to handle and process the CONNECT method request, additional method is needed in HTTPSHandler class.
#tornado.web.asynchronous
def connect(self):
print "some specific processings here"
Finally, I took stupid mistakes in regular expressions and ssl_option.
app2 = Application([(r"https:.*", HTTPSHandler),]) # not correct
app2 = Application([(r".*", HTTPSHandler),]) # correct
httpServer = HTTPServer(app2) # ssl_options is not needed
In theory you should be able to implement the CONNECT method in the same way that WebSocketHandler works, by hijacking the underlying connection's IOStream. But be warned that this is uncharted territory; the HTTP proxy protocol has some differences from plain HTTP and I don't know how well it will work to implement a proxy on top of a normal application-level HTTP service.

Access the response object in a bottlepy after_request hook

I have the following web app:
import bottle
app = bottle.Bottle()
#app.route('/ping')
def ping():
print 'pong'
return 'pong'
#app.hook('after_request')
def after():
print 'foo'
print bottle.response.body
if __name__ == "__main__":
app.run(host='0.0.0.0', port='9999', server='cherrypy')
Is there a way to access the response body before sending the response back?
If I start the app and I query /ping, I can see in the console that the ping() and the after() function run in the right sequence
$ python bottle_after_request.py
Bottle v0.11.6 server starting up (using CherryPyServer())...
Listening on http://0.0.0.0:9999/
Hit Ctrl-C to quit.
pong
foo
but when in after() I try to access response.body, I don't have anything.
In Flask the after_request decorated functions take in input the response object so it's easy to access it. How can I do the same in Bottle?
Is there something I'm missing?
Is there a way to access the response body before sending the response back?
You could write a simple plugin, which (depending on what you're actually trying to do with the response) might be all you need.
Here's an example from the Bottle plugin docs, which sets a request header. It could just as easily manipulate body.
from bottle import response, install
import time
def stopwatch(callback):
def wrapper(*args, **kwargs):
start = time.time()
body = callback(*args, **kwargs)
end = time.time()
response.headers['X-Exec-Time'] = str(end - start)
return body
return wrapper
install(stopwatch)
Hope that works for your purposes.
You can use plugin approach, this is what i did
from bottle import response
class BottlePlugin(object):
name = 'my_custom_plugin'
api = 2
def __init__(self, debug=False):
self.debug = debug
self.app = None
def setup(self, app):
"""Handle plugin install"""
self.app = app
def apply(self, callback):
"""Handle route callbacks"""
def wrapper(*a, **ka):
"""Encapsulate the result in the expected api structure"""
# Check if the client wants a different format
# output depends what you are returning from view
# in my case its dict with keys ("data")
output = callback(*a, **ka)
data = output["data"]
paging = output.get("paging", {})
response_data = {
data: data,
paging: paging
}
# in case if you want to update response
# e.g response code
response.status = 200
return response_data
return wrapper

Bottle.py error routing

Bottle.py ships with an import to handle throwing HTTPErrors and route to a function.
Firstly, the documentation claims I can (and so do several examples):
from bottle import error
#error(500)
def custom500(error):
return 'my custom message'
however, when importing this statement error is unresolved but on running the application ignores this and just directs me to the generic error page.
I found a way to get around this by:
from bottle import Bottle
main = Bottle()
#Bottle.error(main, 500)
def custom500(error):
return 'my custom message'
But this code prevents me from embedding my errors all in a separate module to control the nastiness that would ensue if I kept them in my main.py module because the first argument has to be a bottle instance.
So my questions:
Has anyone else experienced this?
why doesn't error seem to resolve in only my case (I installed from pip install bottle)?
Is there a seamless way to import my error routing from a separate python module into the main application?
If you want to embed your errors in another module, you could do something like this:
error.py
def custom500(error):
return 'my custom message'
handler = {
500: custom500,
}
app.py
from bottle import *
import error
app = Bottle()
app.error_handler = error.handler
#app.route('/')
def divzero():
return 1/0
run(app)
This works for me:
from bottle import error, run, route, abort
#error(500)
def custom500(error):
return 'my custom message'
#route("/")
def index():
abort("Boo!")
run()
In some cases I find it's better to subclass Bottle. Here's an example of doing that and adding a custom error handler.
#!/usr/bin/env python3
from bottle import Bottle, response, Route
class MyBottle(Bottle):
def __init__(self, *args, **kwargs):
Bottle.__init__(self, *args, **kwargs)
self.error_handler[404] = self.four04
self.add_route(Route(self, "/helloworld", "GET", self.helloworld))
def helloworld(self):
response.content_type = "text/plain"
yield "Hello, world."
def four04(self, httperror):
response.content_type = "text/plain"
yield "You're 404."
if __name__ == '__main__':
mybottle = MyBottle()
mybottle.run(host='localhost', port=8080, quiet=True, debug=True)

Categories

Resources