Open Event API Server uses different decorators to control permissions for different access levels as discussed here. Next challenging thing for permissions was reducing redundancy and ensuring permission decorators are independent of different API views. They should not look to the view for which they are checking the permission or some different logic for different views.
In API Server, we have different endpoints that leads to same Resource this way we maintain relationships between different entities but this leads to a problem where permission decorators has to work on different API endpoints that points to different or same resource and but to check a permission some attributes are required and one or more endpoints may not provide all attributes required to check a permission.
For instance, PATCH /session/id` request requires permissions of a Co-Organizer and permission decorator for this requires two things, user detail and event details. It is easy to fetch user_id from logged in user while it was challenging to get “event_id”. Therefore to solve this purpose I worked on a module named “permission_manager.py” situated at “app/api/helpers/permission_manager.py” in the codebase
Basic Idea of Permission Manager
Permission manager basically works to serve the required attributes/view_kwargs to permission decorators so that these decorators do not break
Its logic can be described as:
- It first sits in the middle of a request and permission decorator
- Evaluates the arguments passed to it and ensure the current method of the request (POST, GET, etc ) is the part of permission check or not.
- Uses two important things, fetch and fetch_as
fetch => value of this argument is the URL parameter key which will be fetched from URL or the database ( if not present in URL )
fetch_as => the value received from fetch will be sent to permission decorator by the name as the value of this option.
- If the fetch key is not there in URL, It uses third parameter model which is Model if the table from where this key can be fetched and then passes it to permission decorator
- Returns the requested view on passing access level and Forbidden error if fails
This way it ensures that if looks for the only specific type of requests allowing us to set different rules for different methods.
if 'methods' in kwargs: methods = kwargs['methods'] if request.method not in methods: return view(*view_args, **view_kwargs)
Implementing Permission Manager
Implementing it was a simple thing,
- Firstly, registration of JSON API app is shifted from app/api/__init__.py to app/api/bootstrap.py so that this module can be imported anywhere
- Added permission manager to the app_v1 module
- Created permission_manager.py in app/api/helpers
- Added it’s usage in different APIs
An example Usage:
decorators = (api.has_permission('is_coorganizer', fetch='event_id', fetch_as="event_id", methods="POST", check=lambda a: a.get('event_id') or a.get('event_identifier')),)
Here we are checking if the request has the permission of a Co-Organizer and for this, we need to fetch event_id from request URI. Since no model is provided here so it is required for event_id in URL this also ensures no other endpoints can leak the resource. Also here we are checking for only POST requests thus it will pass the GET requests as it is no checking.
What’s next in permission manager?
Permission has various scopes for improving, I’m still working on a module as part of permission manager which can be used directly in the middle of views and resources so that we can check for permission for specific requests in the middle of any process.
The ability to add logic so that we can leave the check on the basis of some logic may be adding some lambda attributes will work.
- Permission Manager on flask-rest-jsonapi docs: