Managing Related Endpoints in Permission Manager of Open Event API Server
Open Event API Server has its permission manager to manage all permission to different endpoints and some of the left gaps were filled by new helper method has_access. The next challenge for permission manager was to incorporate a feature many related endpoints points to the same resource.
Example:
- /users-events-roles/<int:users_events_role_id>/user or
- /event-invoices/<int:event_invoice_id>/user
Both endpoints point to Users API where they are fetching the record of a single user and for this, we apply the permission “is_user_itself”. This permission ensures that the logged in user is the same user whose record is asked through the API and for this we need the “user_id” as the “id” in the permission function, “is_user_itself”
Thus there is need to add the ability in permission manager to fetch this user_id from different models for different endpoints. For example, if we consider above endpoints then we need the ability to get user_id from UsersEventsRole and EventInvoice models and pass it to permission function so that it can use it for the check.
Adding support
To add support for multiple keys, we have to look for two things.
- fetch_key_url
- model
These two are key attributes to add this feature, fetch_key_url will take the comma separated list which will be matched with view_kwargs and model receives the array of the Model Classes which will be used to fetch the related records from the model
This snippet provides the main logic for this:
for index, mod in enumerate(model): if is_multiple(fetch_key_url): f_url = fetch_key_url[index] else: f_url = fetch_key_url try: data = mod.query.filter(getattr(mod, fetch_key_model) == view_kwargs[f_url]).one() except NoResultFound, e: pass else: found = True if not found: return NotFoundError({'source': ''}, 'Object not found.').respond()
From the above snippet we are:
- We iterate through the models list
- Check if fetch_key_url has multiple keys or not
- Get the key from fetch_key_url on the basis of multiple keys or single key in it.
- We try to attempt to get object from model for the respective iteration
- If there is any record/object in the database then it’s our data. Skipping further process
- Else continue iteration till we get the object or to the end.
To use multiple mode
Instead of providing the single model to the model option of permission manager, provide an array of models. Also, it is optional to provide comma separated values to fetch_key_url
Now there can be scenario where you want to fetch resource from database model using different keys present on your view_kwargs
for example, consider these endpoints
- `/notifications/<notification_id>/event`
- `/orders/<order_id>/event`
Since they point to same resource and if you want to ensure that logged in user is organizer then you can use these two things as:
- fetch_key_url=”notification_id, order_id”
- model=[Notification, Order]
Permission manager will always match indexes in both options, the first key of fetch_key_url will be only used for the first key of the model and so on.
Also, fetch_key_url is an optional parameter and even in multiple mode you can provide a single value as well. But if you provide multiple commas separated values make sure you provide all values such that no of values in fetch_key_url and model must be equal.
Resources
- Flask-rest-jsonapi Permissions
http://flask-rest-jsonapi.readthedocs.io/en/latest/permission.html - Permission Manager Code
https://github.com/fossasia/open-event-orga-server/blob/nextgen/app/api/helpers/permission_manager.py - Learn more about JWT Authorization
https://jwt.io/introduction/