How to do Flask REST api key validation without decorators? - rest

I'm new to Flask and I'm trying to accomplish the following:
For all of the subroutes of a particular route I want to extract a parameter and validate that an api key has been provided either by GET param or Header key.
The ideal would be if I could nest blueprints. Then I would do something like following:
Have a main blueprint to pull the parameter and validate the api key:
#secured_api.url_value_preprocessor
def pull_tenant(endpoint, values):
g.tenant_code = values.pop('tenant')
#secured_api.before_request
def validate_api_key():
api_key = request.headers.get('X-Api-Key')
...
if (api_key is None):
raise InvalidApiKey()
Then, having another blueprint with my resources (v1_bp) I could do:
secured_api.register_blueprint(v1_bp, url_prefix="/v1")
app.register_blueprint(secured_api, url_prefix='/secured/<tenant>')
So that all of v1_bp routes would be under /secured//v1
What would be the best way to achieve this?
Thanks in advance!

Related

Different OpenAPI schema in FastAPI depending on environment

We have a FastApi application that is hosted behind a reverse proxy.
The proxy authenticates the user using Kerberos and adds a X-Remote-User HTTP header to the request.
This header is required by the FastApi application. Here is an example route:
#app.get("/user/me")
async def get_user_me(x_remote_user: str = Header(...)):
return {"User": x_remote_user}
The X-Remote-User header is required for the request which is expected behavior.
When we now open the Swagger Ui, the header is documented and when clicking on "Try it out", we can provide the header value.
This behavior is great for development, but in all other cases it is undesired, because that header is provided by the reverse proxy. For instance, we generate clients using OpenAPI Generator and the clients then all require the X-Remote-User parameter in their requests.
Hence, it would be useful to have a configuration that distinguishes between the environments. If we are behind a reverse proxy, then the generated OpenAPI Schema by FastApi should not include the X-Remote-Header, otherwise if we are in development, it should be included.
What I did so far:
I checked the documentation about security and also some source code of these modules, but I was not able to find a solution.
In the documentation, I read the section Behind a Proxy, but nothing there points me to a potential solution.
I also read about Middleware, but again, no solution.
We could change the generated OpenApi schema. I sketched this in my answer below, but this is not a very elegant solution
Does anyone have a good solution to this problem?
We can use APIKeyHeader to remove the X-Remote-User header from the API signature, but still enforcing the header to be present.
from fastapi.security import APIKeyHeader
apiKey = APIKeyHeader(name="X-Remote-User")
#app.get("/user/me")
async def get_user_me(x_remote_user: str = Depends(apiKey)):
return {"User": x_remote_user}
When the header is not present, we get a "403 Forbidden". If it is present, we retrieve the header value.
The Swagger UI now has a button "Authorize" where we can fill-in the value of the X-Remote-User for testing purposes.
One approach is to generate the OpenApi schema as described in the documentation Extending OpenAPI. After the generation, remove the X-Remote-User from the schema. In the configuration could be a flag that the application it is behind a reverse proxy to execute the code conditionally:
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from MyConfig import Config
app = FastAPI()
#app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
if Config.reverse_proxy:
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom title",
version="2.5.0",
description="This is a very custom OpenAPI schema",
routes=app.routes,
)
// remove X-Remote-User here
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
However this is not a very elegant solution, as we need to parse the Json string and remove the different deeply-nested occurrences of the X-Remote-User header everywhere. This is prone to bugs resulting in an invalid schema. Furthermore it could break if new Rest endpoints are added.
A new param will be soon available for Header, Query and other to exclude elements from the openAPI output: include_in_schema=False
Example:
def test(x_forwarded_for: str = Header(None, include_in_schema=False)):
...
Here the patch state: https://github.com/tiangolo/fastapi/pull/3144

QLIK Sense - REST api chain call

I need to integrate data in my Qlik Sense project using cloud REST api.
I need to call a chain of API as I firstly need the Token
Basically:
1) "Token" REST passing user+psw getting token
2) "API2" REST passing token received from 1 in the BODY
I think I need to use the data script feature, I'm able to create separately the 2 REST call, but how can I pass tokn dinamically in the Body?
Is there a specific code to be added?
Thx
Find an answer here:
https://community.qlikview.com/thread/224957
Basically just edit and parse Body variable:
let vRequestBody = '{"call":"ListarCategorias","app_key":"XXXXXXXX","app_secret":"XXXXXXXXXX","param":[{"pagina":"$(vPagina)","registros_por_pagina":100,"apenas_importado_api":"N"}]}';
let vRequestBody = replace(vRequestBody,'"', chr(34)&chr(34));
and use this at the end of "RestConnectorMasterTable" default scripting snippet WITH CONNECTION(BODY "$(vRequestBody)"):
RestConnectorMasterTable:
SQL SELECT
"__KEY_root",
(SELECT
"codigo",
"totalizadora",
"transferencia",
"__FK_categoria_cadastro"
FROM "categoria_cadastro" FK "__FK_categoria_cadastro")
FROM JSON (wrap on) "root" PK "__KEY_root"
WITH CONNECTION(BODY "$(vRequestBody)");

How to append a prefix to a querystring parameter in AWS API Gateway?

When setting up URL Query String Parameters in the Integration Request part of an API method, it looks like I have the following options
reference a value from method.request.{path|querystring|header}.{var-name}
use a fixed single-quoted string
Even though API Gateway allows complex mappings on the body via VTL, it looks like querystring, header, and path variables do not have this option.
The specific use case I have is I want to populate the prefix query parameter for a call to S3's REST API with something like: 'read'+method.request.path.folder, so all GET requests start under a prefix (without the user having to specify that prefix).
Is there a way for me to achieve this goal using API Gateway?

How to add query parameter to routes in Lumen?

I am trying to know how to add query parameters to routes in Lumen
this is an example of a route I created
$app->get('/product/{apikey}','ProductController#getProduct');
This works when I use
http://api.lumenbased.com/product/10920918
but I would like to use it like this
http://api.lumenbased.com/product/?apikey=10920918
I tried this
$app->get('/product/?apikey={apikey}','ProductController#getProduct');
But this gives me MethodNotAllowedHttpException
I would like to know how to write routes with query parameters in Lumen ?
Just do:
$app->get('/product','ProductController#getProduct');
and use:
$request->get('apikey')
in the ProductController#getProduct function.
(That said, validating an API key is better done via middleware...)

How can I define an Angular resource that doesn't use query parameters for REST GET?

Currently I have a resource defined in Angular like this:
Users: $resource(APIBASEROUTE +'/users/:_id', {_id: '#_id'})
When I call for a user with this code:
$scope.user = ayApi.Users.get({id:$routeParams.userId});
The request is sent as a query parameter like this:
http://localhost:3000/api/v1/users?id=526eff826a6100fb22000000
However it needs to hit the REST server like this:
http://localhost:3000/api/v1/users/526eff826a6100fb22000000
How do I make Angular do it this way?
I guess that the problem is in incosistency of the parameters names.
in definition you have: {_id:'#_id'}), it should be the {id...
Or in your call it should be with underscore
$scope.user = ayApi.Users.get({_id:$routeParams.userId});
Any other parameter (not mapped in the resource definition) is treated as a query string param. That's why angular decided to append your id as a query string part. It is not _id