uvicorn not optional

This commit is contained in:
Maxime Alves LIRMM@home 2020-07-03 22:15:25 +02:00
parent 5280b45b8d
commit b879840491
2 changed files with 44 additions and 36 deletions

View File

@ -1,10 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from functools import wraps
""" Base ACL module that contains generic functions for domains ACL """ Base ACL module that contains generic functions for domains ACL
""" """
def connected(func): def connected(func):
""" Decorator that checks if the user object of the request has been set """ Decorator that checks if the user object of the request has been set
""" """
@wraps(func)
def caller(req, *args, **kwargs): def caller(req, *args, **kwargs):
try: try:
getattr(req.user, 'is_authenticated') getattr(req.user, 'is_authenticated')

View File

@ -50,8 +50,8 @@ def match_route(app: ASGIApp, scope: Scope):
""" """
""" The *result* variable is fitted to the filter that will be applied when """ The *result* variable is fitted to the filter that will be applied when
searching the route in the database. searching the route in the database.
Refer to the database documentation for more details on the api.route Refer to the database documentation for more details on the api.route
table. table.
""" """
result = { result = {
@ -69,7 +69,7 @@ def match_route(app: ASGIApp, scope: Scope):
domain : organigramme domain : organigramme
path : laboratoire/personnel path : laboratoire/personnel
""" """
_, result['version'], result['domain'], path = scope['path'].split('/', 3) _, result['domain'], path = scope['path'].split('/', 2)
except ValueError as e: except ValueError as e:
#404 Not found #404 Not found
raise HTTPException(404) raise HTTPException(404)
@ -78,10 +78,6 @@ def match_route(app: ASGIApp, scope: Scope):
path = f'/{path}' path = f'/{path}'
for route in app.routes: for route in app.routes:
# Parse all routes
match = route.matches(scope)
if match[0] != Match.FULL:
continue
if type(route) != Mount: if type(route) != Mount:
""" The root app should not have exposed routes, """ The root app should not have exposed routes,
@ -90,10 +86,16 @@ def match_route(app: ASGIApp, scope: Scope):
continue continue
""" Clone the scope to assign the path to the path without the """ Clone the scope to assign the path to the path without the
matching domain. matching domain, be careful to the "root_path" of the mounted domain.
@TODO
Also, improper array unpacking may make crash the program without any
explicit error, we may have to improve this as we only rely on this
function to accomplish all the routing
""" """
subscope = scope.copy() subscope = scope.copy()
subscope['path'] = path _, result['domain'], subpath = path.split('/', 2)
subscope['path'] = f'/{subpath}'
for mount_route in route.routes: for mount_route in route.routes:
# Parse all domain routes # Parse all domain routes
@ -102,8 +104,11 @@ def match_route(app: ASGIApp, scope: Scope):
continue continue
# Route matches # Route matches
result['name'] = submatch[1]['endpoint'].__name__ try:
result['http_verb'] = scope['method'] result['name'] = submatch[1]['endpoint'].__name__
result['http_verb'] = scope['method']
except Exception as e:
print(e)
return result, submatch[1]['path_params'] return result, submatch[1]['path_params']
@ -115,9 +120,9 @@ class AclCallerMiddleware(BaseHTTPMiddleware):
""" Points out to the domain which ACL function it should call """ Points out to the domain which ACL function it should call
Parameters : Parameters :
- request (Request): The current request - request (Request): The current request
- call_next (RequestResponseEndpoint): The next middleware/route function - call_next (RequestResponseEndpoint): The next middleware/route function
Return: Return:
@ -127,7 +132,7 @@ class AclCallerMiddleware(BaseHTTPMiddleware):
if scope['type'] != 'http': if scope['type'] != 'http':
await self.app(scope, receive, send) await self.app(scope, receive, send)
return return
if scope['path'].split('/')[-1] not in ['docs','openapi.json','redoc']: if scope['path'].split('/')[-1] not in ['docs','openapi.json','redoc']:
# routes in the the database, the others being # routes in the the database, the others being
@ -136,33 +141,22 @@ class AclCallerMiddleware(BaseHTTPMiddleware):
d_match, path_params = match_route(app, scope) d_match, path_params = match_route(app, scope)
except HTTPException: except HTTPException:
return NotFoundResponse() return NotFoundResponse()
except Exception as e:
raise e
d_match, path_params = match_route(app, scope) d_match, path_params = match_route(app, scope)
try: try:
scope['acls'] = [] scope['acls'] = []
"""
for acl in AclView(**d_match).select(): for acl in AclView(**d_match).select():
# retrieve related ACLs
if ('acl_function_name' not in acl.keys() if ('acl_function_name' not in acl.keys()
or 'domain' not in acl.keys()): or 'domain' not in acl.keys()):
continue continue
scope['acls'].append(acl['acl_function_name']) scope['acls'].append(acl['acl_function_name'])
acl_module = importlib.import_module(
'.acl',
'organigramme'
)
try:
acl_functions.append(
getattr(acl_module.acl, acl_function_name))
except AttributeError:
if True: #function(AUTH, path_params):
response = await call_next(request)
break
"""
except StopIteration: except StopIteration:
# TODO : No ACL sur une route existante, prevenir l'admin? # TODO : No ACL sur une route existante, prevenir l'admin?
print("No ACL") print("No ACL")
@ -179,7 +173,7 @@ def mount_domains(app: Starlette, domains: list):
- app (FastAPI): The FastAPI object - app (FastAPI): The FastAPI object
- domains (list): The domains to mount, retrieved from the database - domains (list): The domains to mount, retrieved from the database
with their attributes "version", "name" with their attributes "version", "name"
Returns: Nothing Returns: Nothing
""" """
@ -189,8 +183,12 @@ def mount_domains(app: Starlette, domains: list):
# Retrieve domain app according to domain details # Retrieve domain app according to domain details
try: try:
domain_app = importlib.import_module( print(f'Will import {domain["name"]}.app:app')
f'{domain["name"]}.app').app #@TODO let the configuration come from the domain module - (or apidb)
environ["HALFORM_DSN"] = "dbname=si user=si"
domain_mod = importlib.import_module(
f'{domain["name"]}.app')
domain_app = domain_mod.app
except ModuleNotFoundError: except ModuleNotFoundError:
sys.stderr.write( sys.stderr.write(
f'Could not find module *{domain["name"]}* in sys.path\n') f'Could not find module *{domain["name"]}* in sys.path\n')
@ -198,6 +196,11 @@ def mount_domains(app: Starlette, domains: list):
except ImportError: except ImportError:
sys.stderr.write(f'Could not import *app* from *{domain}*') sys.stderr.write(f'Could not import *app* from *{domain}*')
continue continue
except Exception as e:
sys.stderr.write(f'Error in import *{domain["name"]}*\n')
print(e)
continue
# Alter the openapi_url so the /docs page doesn't try to get # Alter the openapi_url so the /docs page doesn't try to get
# /{name}/openapi.json (@TODO : retport the bug to FastAPI) # /{name}/openapi.json (@TODO : retport the bug to FastAPI)
@ -205,18 +208,21 @@ def mount_domains(app: Starlette, domains: list):
# Mount the domain app on the prefix # Mount the domain app on the prefix
# e.g. : /v4/organigramme # e.g. : /v4/organigramme
app.mount('/{version}/{name}'.format(**domain), domain_app) try:
app.mount('/{version}/{name}'.format(**domain), app=domain_app)
except Exception as e:
print(f'Failed to mount *{domain}*\n')
def startup(): def startup():
global app
# Mount the registered domains # Mount the registered domains
try: try:
domains_list = [elt for elt in Domain().select()] domains_list = [elt for elt in Domain().select()]
mount_domains(app, domains_list) mount_domains(app, domains_list)
except Exception as e: except Exception as e:
sys.stderr.write('Error in the *domains* retrieval') sys.stderr.write('Error in the *domains* retrieval\n')
sys.stderr.write(str(e)) raise e
sys.exit(1)
async def root(request): async def root(request):
return JSONResponse({'payload': request.payload}) return JSONResponse({'payload': request.payload})