How to access request in Flask Middleware - python

I want to access request.url in middleware.
Flask app - test.py
from flask import Flask
from middleware import TestMiddleware
app = Flask(__name__)
app.wsgi_app = TestMiddleware(app.wsgi_app)
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
middleware.py:
from flask import request
class TestMiddleware(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
# How do I access request object here.
print "I'm in middleware"
return self.app(environ, start_response)
I understand request can be accessed in Flask application context. We normally use
with app.test_request_context()
But in middleware, I don't have access to Flask app object.
How do I proceed?
Thanks for any help..

It's the application object that constructs the request object: it doesn't exist until the app is called, so there's no way for middleware to look at it beforehand. You can, however, construct your own request object within the middleware (using Werkzeug directly rather than Flask):
from werkzeug.wrappers import Request
req = Request(environ, shallow=True)
You might even be able to construct Flask's own Request object (flask.wrappers.Request, which is a subclass of Werkzeug's Request class) the same way. Looking at the source I don't see anything that should stop you from doing this, but since it isn't designed to be used that way you're probably best off sticking with the Werkzeug one unless you need one of the extra properties added by Flask's subclass.

Middleware stands between your WSGI server and Flask Application. The request object is created in the Flask Application. So there isn't any request object in the middleware.
Perhaps you need a #before_request handler called just before your view?

You can't have a Request object before the application finds an URL rule and creates it. However, after the application has done its thing, you can find the Request object in the environ:
environ['werkzeug.request']

Related

flask binding resource to an endpoint

as i know so far there are two ways of binding resouces to an endpoint using flask framework,
the first one is using the #app.route decorator, like this:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
the second way is to create a class that inherite from Resources in flask-restfull, this class contains the http methods as functions, we bind it to an endpoint using add_resource method, as follow:
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return 'Hello, World!'
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run()
as I understand this two different syntaxes do the same thing, what I dont understand is what is the difference between the two ? or is one designed for a specific type of applications and the other to another type of applications ?
Flask-RESTful is an extension of Flask, which itself is built on many of the excellent utilities provided by Werkzeug.
from flask import Flask
app = Flask(__name__)
#app.route('/foo')
def say_foo():
return 'foo'
#app.route('/bar')
def say_bar():
return 'bar'
if __name__ == '__main__':
app.run()
One of the biggest ideas behind REST is using HTTP to interact with resources. The problem with this code is our resource is split up over multiple methods. There's no encapsulation. While the API itself incorporates basic elements of REST, the code completely fails to capture these ideas. This is bad! There's no reason our internal code shouldn't match the external appearance of our API.
Using Flask-RESTful
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class Foo(Resource):
def get(self):
return 'foo'
class Bar(Resource)
def get(self):
return 'bar'
# As you might have guessed, these two lines add a given resource to our API at the
# specified route. We no longer need to enumerate what methods a route supports,
# since Flask-RESTful resolves this information by inspecting what methods you've
# defined on your resource object.
api.add_resource(Foo, '/foo')
api.add_resource(Bar, '/bar')
if __name__ == '__main__':
app.run()
We have classes now! This is a huge deal. Our routes now map directly to objects. Even better, the methods on a given class are exactly the same as their HTTP counterparts. We no longer have to deal with naming methods on our routes like say_foo, since there's a 1 to 1 mapping between HTTP methods and methods on our classes.
read more: https://dougblack.io/words/flask-restful-101.html

Configure Email Token with Flask Factory Application create_app

I am getting confused with configurations and imports once I started using the Flask factory application pattern.
I am creating an application with the function create_app in #app/init.py
I have a config file for setting the development/testing/production variables, and an instance folder with another config file.
def create_app(config_name):
app=Flask(__name__, instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('config.py')
etc...
return app
I am using blueprints and have an authentication view in #app/auth/views.py
I am trying to set up email confirmation tokens using URLSafeTimedSerializer...
from itsdangerous import URLSafeTimedSerializer
#auth.route('/register', methods=['GET','POST'])
def register():
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'])
token = ts.dumps(self.email, salt='email-confirm-key')
etc...
Now my problem is, my variable 'ts' needs the app.config['SECRET_KEY'] set. But I am unable to define the app variable (as is shown in all online tutorials). I get an error when I try to import...(in #app/auth/views.py)
from .. import app
and when I try to import like...
from .. import create_app
Can someone shine light on how to initialize modules using 'app' and app.config outside the flask app factory create_app?
Hope you understand my question.
In this scenario you should use Flask.current_app
from flask import current_app
...
ts = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
From the documentation:
flask.current_app
Points to the application handling the request. This
is useful for extensions that want to support multiple applications
running side by side. This is powered by the application context and
not by the request context, so you can change the value of this proxy
by using the app_context() method.
This link aso explains further details about the Flask application factory methodology, in particular using current_app to access the app configuration.

How to protect custom endpoints using BasicAuth?

Say I have enabled authentication to the resources using BasicAuth:
class MyBasicAuth(BasicAuth):
def check_auth(self,username,password,allowed_roles,resource,method):
return username == 'secretusername' and password == 'secretpass'
I also have custom routes which are used to manage documents from a HTML view. How do I use the same MyBasicAuth to protect the all the custom routes? I also need to implement logic which authenticates using the above MyBasicAuth.
Please help me with this. It's for personal use, so I preferred hard coding the username and password.
If you are trying to use a custom end-point Authentication you will find it difficult as mentioned here:
https://github.com/pyeve/eve/issues/860
I ended up writing a wrapper to get around the issue of 'resource' not being passed to 'requires_auth':
def auth_resource(resource):
def fdec(f):
#wraps(f)
def wrapped(*args, **kwargs):
return f(resource=resource, *args, **kwargs)
return wrapped
return fdec
This way you can define in your DOMAIN an authentication class:
DOMAIN = {
'testendpoint'= {'authentication':MyCustomAuthetication},
'otherendpoints'=...
And in my app I have wrapped the requires_auth decorator and added this as a authentication resource.
#app.route('/testendpoint/<item>', methods=['GET'])
#auth_resource('testendpoint')
#requires_auth('item')
def my_end_point_function(*args, **kwargs):
dosomthinghere
As long as an authentication class is defined in the settings file for an endpoint, this also allows you to reuse any authentication defined in another endpoint which may be handy if you want to make sure all the endpoints use the same authentication.
You can leverage the requires_auth decorator which is used internally by Eve itself. That way, your auth class will also be used to protect your custom routes:
from eve import Eve
from eve.auth import requires_auth
app = Eve()
#app.route('/hello')
#requires_auth('resource')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
If you are using flask blueprints for your custom routes, you can add a before request function for your blueprint to do that.
First, create a function to check authentication from blueprints. You need to get the Authorization header from the flask request by yourself, like this:
from flask import request, abort, current_app
from werkzeug.http import parse_authorization_header
def check_blueprint_auth():
if 'Authorization' not in request.headers:
print('Authorization header not found for authentication')
return abort(401, 'Authorization header not found for authentication')
header = parse_authorization_header(request.headers['Authorization'])
username = None if header is None else header['username']
password = None if header is None else header['password']
return username == 'secretusername' and password == 'secretpass'
Then, you can set this function to be called before each blueprint's request. Below is an example of a blueprint definition, setting the before_request function:
from flask import Blueprint, current_app as app
# your auth function
from auth import check_blueprint_auth
blueprint = Blueprint('prefix_uri', __name__)
# this sets the auth function to be called
blueprint.before_request(check_blueprint_auth)
#blueprint.route('/custom_route/<some_value>', methods=['POST'])
def post_something(some_value):
# something
Finally, you need to bind the blueprint with your eve app. An example on how to bind blueprints, taken in part from here:
from eve import Eve
# your blueprint
from users import blueprint
from flask import current_app, request
app = Eve()
# register the blueprint to the main Eve application
app.register_blueprint(blueprint)
app.run()
Hope that helps.

how do I make get request directly to a flask app object

I inherited a project that uses flask. This flask application has several APIs, each API has a GET function that returns a json object.
I was asked to implement an additional API that requests information from the other APIs. So my question is how do I make a GET request directly to a flask application? Is it something like....
from flask import request
#app.route('/root_dir/api_number_1/info', methods=['GET'])
def request_info_from_api_number_1():
return request.get_json()
#app.route('/root_dir/api_number_2/info', methods=['GET'])
def request_info_from_api_number_2():
return request.get_json()
When I do this the functions return None. I suppose I could always make an http request to the flask url and specify the address as localhost but it seems strange to have to make an http request when I can directly access the flask app object.
Could you just use the existing api functions?
from flask import Flask
app = Flask(__name__)
#app.route("/foo")
def foo():
return "foo"
#app.route("/foobar")
def foobar():
return foo() + "bar"
if __name__ == "__main__":
app.run(debug=True)

Change/Replace immutable request object in Flask

Currently I am working on an Oauthlib-Flask implementation for a non-REST API. But I have two scenarios where I want to change/add a value of the flask request object. Since it is immutable this doesn't come with ease. I tried making a duplicate as it is suggested in Changing values on a werkzeug request object. But since the #oauth.authorize_handler uses the given request object I would have to replace it, which resolves in an UnboundLocalError: local variable 'request' referenced before assignment error. Here is my sample code (it is a part of the implicit grant):
#app.route('/oauth/authorize', methods=['GET', 'POST'])
#login
#oauth.authorize_handler
def authorize(*args, **kwargs):
if request.method == 'GET':
client_id = kwargs.get('client_id')
client = Client.query.filter_by(client_id=client_id).first()
kwargs['client'] = client
return render_template('authorize.html', **kwargs)
r = make_duplicate_request(request)
#Change/add values of r
request = r
return True
Am I doing something wrong or is there another possibility to change the request object?
Thanks for your help!
Update:
The code above describes the situation where I want to pass information to the tokensetter function. This could be done with a global variable, but I wanted to avoid that.
In my signup routine the client sends a request like this to the API:
params_signup = {
"schemas":["urn:scim:schemas:core:2.0:User"],
"expireIn":3600,
"username":"test#web.de",
"password":"123",
"access_token":"",
"externalId":"tmeinhardt",
"grant_type":"password",
"client_id":"1",
"params":{
"age":"20-30",
"gender":"m",
}
}
I need the grant_type and client_id part only for the tokenhandler and wanted to add it manually to the request object. But since this object is immutable...
Writing this for those who will come across this and are trying to implement an OAuth flow.
Don't use decorators use middleware instead
I believe you should handle this in a middleware. In the middleware, you can set the authorization property of the 2nd parameter of the call function which contains the current wsgi app environment variables you have passed in your init function.
Look at code below:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
cookie = Request(environ).cookies.get('access_token')
if cookie is not None:
environ['HTTP_AUTHORIZATION']='Bearer '+cookie
return self.app(environ, start_response)
you can create a new request and splat the headers into a new dict of an HttpRequest (eg. Authlib's HttpRequest)
from flask import request as _req
from authlib.oauth2.rfc6749 import HttpRequest
request = HttpRequest(
_req.method, _req.full_path, _req.data, {**_req.headers, **auth}
)
request.req = _req
then you can work with this new request instead of the Flask request

Categories

Resources