Falcon CORS middleware does not work properly - python

I'm using Falcon CORS to allow access to my web service only from several domains. But it does not work properly.
Let me explain, if we take a look at my implementation:
ALLOWED_ORIGINS = ['*']
crossdomain_origin = CORS(allow_origins_list=[ALLOWED_ORIGINS], log_level='DEBUG')
app = falcon.API(middleware=[RequireJSON(), JSONTranslator(), cors.middleware])
When I make any post request to my API service, I get this warning:
Aborting response due to origin not allowed
But, then I get the correct response from my API.
Here is an official docs about this module: https://github.com/lwcolton/falcon-cors

Your code does not match the falcon-cors documentation's example:
import falcon
from falcon_cors import CORS
cors = CORS(allow_origins_list=['http://test.com:8080'])
api = falcon.API(middleware=[cors.middleware])
# ^^^^^^^^^^^^^^^
Note the cors.middleware variable is being passed into the api call. In your code you are creating crossdomain_origin but not passing it into the API setup.
If this does not solve it, please provide a working code example, including the Falcon resource classes, that is easy to test and reproduce, and I'm happy to try to assist.
edit:
From comments below, it sounds like falcon-cors is working properly, rather the problem may be origin header was being omitted from the request.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
The Origin header indicates the origin of the cross-site access request or preflight request.

I tried as guided by lwcolton on github here
And also set allow_all_headers=True, allow_all_methods=True
i.e. same as #Ryan comment
from falcon_cors import CORS
cors = CORS(
allow_all_origins=True,
allow_all_headers=True,
allow_all_methods=True,
)
api = falcon.API(middleware=[cors.middleware])

Side note:
ORIGIN '*' does not work on some browsers.. notably IE. In the past I've had to dynamically set the ORIGIN header to the 'host' name requested in the HTTP headers in order to support a wildcard domain host for a site I setup.

There's is another way to implement this without using falcon-cors
You might want to look at this on the official documentation - how-do-i-implement-cors-with-falcon
class CORSComponent:
def process_response(self, req, resp, resource, req_succeeded):
resp.set_header('Access-Control-Allow-Origin', '*')
if (req_succeeded
and req.method == 'OPTIONS'
and req.get_header('Access-Control-Request-Method')
):
# NOTE: This is a CORS preflight request. Patch the
# response accordingly.
allow = resp.get_header('Allow')
resp.delete_header('Allow')
allow_headers = req.get_header(
'Access-Control-Request-Headers',
default='*'
)
resp.set_headers((
('Access-Control-Allow-Methods', allow),
('Access-Control-Allow-Headers', allow_headers),
('Access-Control-Max-Age', '86400'), # 24 hours
))
When using the above approach, OPTIONS requests must also be special-cased in any other middleware or hooks you use for auth, content-negotiation, etc. For example, you will typically skip auth for preflight requests because it is simply unnecessary; note that such request do not include the Authorization header in any case.
You can now put this in middleware
api = falcon.API(middleware=[
CORSComponent()
])

Related

How to obtain the caller's IP address in Flask/Connexion?

I'm using an OpenAPI 3.0 specification (swagger.yml) and use Swagger Codegen to create the corresponding Python Flask application stubs. This is how I run the application to expose my Swagger API:
app = connexion.App(__name__, specification_dir='./swagger/')
app.app.json_encoder = encoder.JSONEncoder
app.add_api('swagger.yaml', arguments={'title': 'My Test API'})
# add CORS support to send Access-Control-Allow-Origin header
CORS(app.app)
So far so good. The application logic is handled within the generated Python stubs which are linked by the x-openapi-router-controller: swagger_server.controllers.user_controller.
I now however need to access HTTP Request specific information within the application itself to for example react differently based on the HTTP_CLIENT_IP address
How can I obtain that information within my controller endpoint?
Use Flask's request context.
For example, to get the HTTP_CLIENT_IP, use:
from flask import request
http_client_ip = request.remote_addr
You can read more about request here.
Attached two related links addressing the same issue on request header parameters and how connexion does not forward them to custom controllers. I ended up manually accessing them via
access_token = connexion.request.headers['access_token']

Using https as standard with django project

I am learning django and trying to complete my first webapp.
I am using shopify api & boilder plate (starter code) and am having an issue with the final step of auth.
Specifically, the redirect URL -- it's using HTTP:// when it should NOT and I don't know how to change it..
#in my view
def authenticate(request):
shop = request.GET.get('shop')
print('shop:', shop)
if shop:
scope = settings.SHOPIFY_API_SCOPE
redirect_uri = request.build_absolute_uri(reverse('shopify_app_finalize')) #try this with new store url?
print('redirect url', redirect_uri) # this equals http://myherokuapp.com/login/finalize/
permission_url = shopify.Session(shop.strip()).create_permission_url(scope, redirect_uri)
return redirect(permission_url)
return redirect(_return_address(request))
Which is a problem because my app uses the Embedded Shopify SDK which causes this error to occur at the point of this request
Refused to frame 'http://my.herokuapp.com/' because it violates the following Content Security Policy directive: "child-src 'self' https://* shopify-pos://*". Note that 'frame-src' was not explicitly set, so 'child-src' is used as a fallback.
How do i change the URL to use HTTPS?
Thank you so much in advance. Please let me know if I can share any other details but my code is practically identical to that starter code
This is what the Django doc says about build_absolute_uri:
Mixing HTTP and HTTPS on the same site is discouraged, therefore
build_absolute_uri() will always generate an absolute URI with the
same scheme the current request has. If you need to redirect users to
HTTPS, it’s best to let your Web server redirect all HTTP traffic to
HTTPS.
So you can do two things:
Make sure your site runs entirely on HTTPS (preferred option): Setup your web server to use HTTPS, see the Heroku documentation on how to do this. Django will automatically use HTTPS for request.build_absolute_uri if the incoming request is on HTTPS.
I'm not sure what gets passed in the shop parameter but if it contains personal data I'd suggest to use HTTPS anyway.
Create the URL yourself:
url = "https://{host}{path}".format(
host = request.get_host(),
path = reverse('shopify_app_finalize'))
But you will still need to configure your server to accept incoming HTTPS requests.

simple Authentication and ACL using cornice

I have a RESTful API written in pyramid/cornice. It provides an API for an Ember client.
I have followed the cornice tutorial and have a valid_token validator which I use on many views as methods of resource classes.
def valid_token(request):
header = 'Authorization'
token = request.headers.get(header)
if token is None:
request.errors.add('headers', header, "Missing token")
request.errors.status = 401
return
session = DBSession.query(Session).get(token)
if not session:
request.errors.add('headers', header, "invalid token")
request.errors.status = 401
request.validated['session'] = session
Now I want to start selectively protecting resources. The Pyramid way seems to be to register authentication/authorization policies. The ACLAuthorizationPolicy seems to provide access to the nice ACL tooling in pyramid. However, it seems that pyramid needs both authentication and authorization policies to function. Since I'm authenticating with my validator this is confusing me.
Can I use ACL to control authorization whilst authenticating using my cornice valid_token validator? Do I need to register pyramid authentication or authorization policies?
I'm a bit confused, having little experience of using ACL in pyramid.
It is not an easy question :)
Shortly:
What you implemented in your validator is already taken care of by Pyramid with an AuthenticationPolicy
Start setting up a SessionAuthenticationPolicy with your custom callback (see code)
Once this authn setup, you will have those 401 responses, and your session value in the request.authenticated_userid attribute. You can also custom stuff in the request.registry object.
The only reason to keep your validator is if you want to return the invalid token messages in the 401 response. But for that, you can define a custom 401 pyramid view (using #forbidden_view_config)
Once you have that, you can setup a custom authorization for your views. You can find a very simple example in Cliquet first versions here : authz code and view perm
Good luck!
You may wanna do something like:
from pyramid.authentication import SessionAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from your_module import valid_token
authn_policy = SessionAuthenticationPolicy(debug=True, callback=valid_token)
authz_policy = ACLAuthorizationPolicy()
config = Configurator(authentication_policy=authn_policy,authorization_policy=authz_policy)
And ofcourse in the Configuration will receive other arguments like settigns, locale_negociator, ...........
Hope this will help

Google App Engine inter module communication authorization (python)

Citing Google App Engine inter module communication authorization the problem I have is that in the Docs (communication between modules) says:
You can configure any manual or basic scaling module to accept
requests from other modules in your app by restricting its handler to
only allow administrator accounts, specifying login: admin for the
appropriate handler in the module's configuration file. With this
restriction in place, any URLFetch from any other module in the app
will be automatically authenticated by App Engine, and any request
that is not from the application will be rejected.
And this is exactly the configuration I have for my module called "api1". In my app.yaml file I have:
# can accept requests from other modules.
# with login: admin and they are authenticated automatically.
- url: /.*
script: _go_app
login: admin
I'm trying now, from a different module in the same app, to make a service call as suggested in the doc using urfetch.fetch() method, and my implementation is:
from google.appengine.api import urlfetch, modules, app_identity
from rest_framework.response import Response, status
#api_view(['POST'])
def validate_email(request):
url = "http://%s/" % modules.get_hostname(module="api1")
payload = json.dumps({"SOME_KEY":"SOME_VALUE"})
appid = app_identity.get_application_id()
result = urlfetch.fetch(url + "emails/validate/document",
follow_redirects=False,
method=urlfetch.POST,
payload=payload,
headers={"Content-Type":"application/json")
return Response({
'status_code': result.status_code,
'content': result.content
}, status=status.HTTP_200_OK)
According to the documentation, having specified the follow_redirects=False, fetch() will automatically insert an header in my call (I've even tried to add it explicitly) with the "X-Appengine-Inbound-Appid" : MY-APP-ID.
Unfortunately I get as result of the fetch call a 302 redirect, if I follow it, it's a redirect to the authentication form. This occurs in Development server as well as in Production.
Can you please let me know how can I call my api1 service inside my validate_email method (belonging to a different module in the same app)?
Is there another way to authenticate the call since it seems the way suggested inside the documentation is not working?
Thank you
As written here this is a tracked issue now on google appengine public issue tracker. So everyone can go there to check for updates.
In the meanwhile I solved the issue removing the login: admin from the app.yaml and in the handler of my service I've checked manually for the existence of the header X-Appengine-Inbound-Appid and its value.

Proxying to another web service with Flask

I want to proxy requests made to my Flask app to another web service running locally on the machine. I'd rather use Flask for this than our higher-level nginx instance so that we can reuse our existing authentication system built into our app. The more we can keep this "single sign on" the better.
Is there an existing module or other code to do this? Trying to bridge the Flask app through to something like httplib or urllib is proving to be a pain.
I spent a good deal of time working on this same thing and eventually found a solution using the requests library that seems to work well. It even handles setting multiple cookies in one response, which took a bit of investigation to figure out. Here's the flask view function:
from dotenv import load_dotenv # pip package python-dotenv
import os
#
from flask import request, Response
import requests # pip package requests
load_dotenv()
API_HOST = os.environ.get('API_HOST'); assert API_HOST, 'Envvar API_HOST is required'
#api.route('/', defaults={'path': ''}) # ref. https://medium.com/#zwork101/making-a-flask-proxy-server-online-in-10-lines-of-code-44b8721bca6
#api.route('/<path>')
def redirect_to_API_HOST(path): #NOTE var :path will be unused as all path we need will be read from :request ie from flask import request
res = requests.request( # ref. https://stackoverflow.com/a/36601467/248616
method = request.method,
url = request.url.replace(request.host_url, f'{API_HOST}/'),
headers = {k:v for k,v in request.headers if k.lower() == 'host'},
data = request.get_data(),
cookies = request.cookies,
allow_redirects = False,
)
#region exlcude some keys in :res response
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] #NOTE we here exclude all "hop-by-hop headers" defined by RFC 2616 section 13.5.1 ref. https://www.rfc-editor.org/rfc/rfc2616#section-13.5.1
headers = [
(k,v) for k,v in res.raw.headers.items()
if k.lower() not in excluded_headers
]
#endregion exlcude some keys in :res response
response = Response(res.content, res.status_code, headers)
return response
Update April 2021: excluded_headers should probably include all "hop-by-hop headers" defined by RFC 2616 section 13.5.1.
I have an implementation of a proxy using httplib in a Werkzeug-based app (as in your case, I needed to use the webapp's authentication and authorization).
Although the Flask docs don't state how to access the HTTP headers, you can use request.headers (see Werkzeug documentation). If you don't need to modify the response, and the headers used by the proxied app are predictable, proxying is staightforward.
Note that if you don't need to modify the response, you should use the werkzeug.wsgi.wrap_file to wrap httplib's response stream. That allows passing of the open OS-level file descriptor to the HTTP server for optimal performance.
My original plan was for the public-facing URL to be something like http://www.example.com/admin/myapp proxying to http://myapp.internal.example.com/. Down that path leads madness.
Most webapps, particularly self-hosted ones, assume that they're going to be running at the root of a HTTP server and do things like reference other files by absolute path. To work around this, you have to rewrite URLs all over the place: Location headers and HTML, JavaScript, and CSS files.
I did write a Flask proxy blueprint which did this, and while it worked well enough for the one webapp I really wanted to proxy, it was not sustainable. It was a big mess of regular expressions.
In the end, I set up a new virtual host in nginx and used its own proxying. Since both were at the root of the host, URL rewriting was mostly unnecessary. (And what little was necessary, nginx's proxy module handled.) The webapp being proxied to does its own authentication which is good enough for now.

Categories