Improved RBAC support for authentication.requires
See original GitHub issueThe requires scope uses an all check which can be problematic for certain endpoints using RBAC. This is because for endpoints where, say, Data-Science and Sales both have access it can start to get complicated. I wrote this and am currently using it as a solution. It’s backward compatible. I would’ve forked and made a PR but I’m not in it for the glory hah
import asyncio
import functools
import inspect
import typing
from starlette.exceptions import HTTPException
from starlette.requests import HTTPConnection, Request
from starlette.responses import RedirectResponse, Response
from starlette.websockets import WebSocket
def has_required_scope(conn: HTTPConnection, scopes: typing.Sequence[str], match_all: bool = True) -> bool:
if match_all:
return all(scope in conn.auth.scopes for scope in scopes)
else:
return any(scope in conn.auth.scopes for scope in scopes)
def requires(
scopes: typing.Union[str, typing.Sequence[str]],
status_code: int = 403,
redirect: str = None,
requires_all: bool = True
) -> typing.Callable:
scopes_list = [scopes] if isinstance(scopes, str) else list(scopes)
def decorator(func: typing.Callable) -> typing.Callable:
type = None
sig = inspect.signature(func)
for idx, parameter in enumerate(sig.parameters.values()):
if parameter.name == "request" or parameter.name == "websocket":
type = parameter.name
break
else:
raise Exception(
f'No "request" or "websocket" argument on function "{func}"'
)
if type == "websocket":
# Handle websocket functions. (Always async)
@functools.wraps(func)
async def websocket_wrapper(
*args: typing.Any, **kwargs: typing.Any
) -> None:
websocket = kwargs.get("websocket", args[idx])
assert isinstance(websocket, WebSocket)
if not has_required_scope(websocket, scopes_list, requires_all):
await websocket.close()
else:
await func(*args, **kwargs)
return websocket_wrapper
elif asyncio.iscoroutinefunction(func):
# Handle async request/response functions.
@functools.wraps(func)
async def async_wrapper(
*args: typing.Any, **kwargs: typing.Any
) -> Response:
request = kwargs.get("request", args[idx])
assert isinstance(request, Request)
if not has_required_scope(request, scopes_list, requires_all):
if redirect is not None:
return RedirectResponse(url=request.url_for(redirect))
raise HTTPException(status_code=status_code)
return await func(*args, **kwargs)
return async_wrapper
else:
# Handle sync request/response functions.
@functools.wraps(func)
def sync_wrapper(*args: typing.Any, **kwargs: typing.Any) -> Response:
request = kwargs.get("request", args[idx])
assert isinstance(request, Request)
if not has_required_scope(request, scopes_list, requires_all):
if redirect is not None:
return RedirectResponse(url=request.url_for(redirect))
raise HTTPException(status_code=status_code)
return func(*args, **kwargs)
return sync_wrapper
return decorator
Example:
@app.route('/v1/customer/{customer_id}/reports/unique-files')
@requires(['DataScience', 'Manager', 'Sales'], requires_all=False)
async def unique_file_report(request):
Issue Analytics
- State:
- Created 4 years ago
- Reactions:3
- Comments:10 (4 by maintainers)
Top Results From Across the Web
RBAC (Role-Based Access Control): What is it and why use it?
RBAC defined three basic requirements for access control: ... Security: RBAC improves overall security as it relates to compliance, ...
Read more >Authorization using Role-Based Access Control
Before implementing RBAC you should evaluate the security needs of the users in your organization and, based on the resources they require to...
Read more >What is Role-Based Access Control | RBAC vs ACL & ABAC
Access control measures regulate who can view or use resources in a computing system, often relying on authentication or authorization based on log-in ......
Read more >What is Role-Based Access Control (RBAC)? - Varonis
Role-Based Access Control (RBAC) is a security paradigm whereby users are granted access to resources based on their role in the company.
Read more >Authentication and Authorization with RBAC - Couchbase
In March's developer build, you can start to see some major changes to authentication and authorization within Role Based Access Control ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Exactly yes.
If we did do something here, then we’d also want to enforce it yes.
However I wouldn’t want us to consider anything here until we feel like the issue backlog is very much in hand. Let’s keep the focus right now on stability, bug-fixes, and getting everything to a really nicely sustainable workflow.
I think if we were just able to pass in our own
has_required_scope()
to requires, that would open up a lot of flexibility.