auth.rst 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. .. _auth-dev:
  2. Custom authorization
  3. ======================
  4. Our :ref:`authorization` section describes general authorization handling in FlexMeasures.
  5. If you are creating your own API endpoints for a custom energy flexibility service (on top of FlexMeasures), you should also get your authorization right.
  6. It's recommended to get familiar with the decorators we provide. Here are some pointers, but feel free to read more in the ``flexmeasures.auth`` package.
  7. In short, we recommend to use the ``@permission_required_for_context`` decorator (more explanation below).
  8. FlexMeasures also supports role-based decorators, e.g. ``@account_roles_required``. These authorization decorators are more straightforward to use than the ``@permission_required_for_context`` decorator. However, they are a bit crude as they do not distinguish on what the context is, nor do they qualify on the required permission(e.g. read versus write). [#f1]_
  9. Finally, all decorators available through `Flask-Security-Too <https://flask-security-too.readthedocs.io/en/stable/patterns.html#authentication-and-authorization>`_ can be used, e.g. ``@auth_required`` (that's technically only checking authentication) or ``@permissions_required``.
  10. Permission-based authorization
  11. --------------------------------
  12. Via permissions, it's possible to define authorization access to data, distinguishing between create, read, update and delete access. It's a finer model than simply allowing per role.
  13. The data models codify under which conditions a user can have certain permissions to work with their data.
  14. You, as the endpoint author, need to make sure this is checked. Here is an example (taken from the decorator docstring):
  15. .. code-block:: python
  16. @app.route("/resource/<resource_id>", methods=["GET"])
  17. @use_kwargs(
  18. {"the_resource": ResourceIdField(data_key="resource_id")},
  19. location="path",
  20. )
  21. @permission_required_for_context("read", ctx_arg_name="the_resource")
  22. @as_json
  23. def view(resource_id: int, resource: Resource):
  24. return dict(name=resource.name)
  25. As you see, there is some sorcery with ``@use_kwargs`` going on before we check the permissions. `That decorator <https://webargs.readthedocs.io>`_ is relaying to a `Marshmallow <https://marshmallow.readthedocs.io/>`_ field definition. Here, ``ResourceIdField`` is a definition which de-serializes an ID (passed in as a request parameter) into a ``Resource`` instance. This instance can then be asked if the current user may read it. That last part is what ``@permission_required_for_context`` is doing. You can find these Marshmallow fields in ``flexmeasures.api.common.schemas``.
  26. Account roles
  27. ---------------
  28. Another way to implement custom authorization is to define custom account roles. E.g. if several services run on one FlexMeasures server, each service could define a "MyService-subscriber" account role.
  29. To make sure that only users of such accounts can use the endpoints:
  30. .. code-block:: python
  31. @flexmeasures_ui.route("/bananas")
  32. @account_roles_required("MyService-subscriber")
  33. def bananas_view:
  34. pass
  35. .. note:: This endpoint decorator lists required roles, so the authenticated user's account needs to have each role. You can also use the ``@account_roles_accepted`` decorator. Then the user's account only needs to have at least one of the roles.
  36. User roles
  37. ---------------
  38. There are also decorators to check user roles. Here is an example:
  39. .. code-block:: python
  40. @flexmeasures_ui.route("/bananas")
  41. @roles_required("account-admin")
  42. def bananas_view:
  43. pass
  44. .. note:: You can also use the ``@roles_accepted`` decorator.
  45. .. rubric:: Footnotes
  46. .. [#f1] Some authorization features are not possible for endpoints decorated in this way. For instance, we have an ``admin-reader`` role who should be able to read but not write everything ― with only role-based decorators we can not allow this user to read (as we don't know what permission the endpoint requires).