flexmeasures.auth.decorators
Auth decorators for endpoints
Functions
- flexmeasures.auth.decorators.account_roles_accepted(*account_roles)
Decorator which specifies that a user’s account must have at least one of the specified roles (or must be an admin). Example:
@app.route(‘/postMeterData’) @account_roles_accepted(‘Prosumer’, ‘MDC’) def post_meter_data():
return ‘Meter data posted’
The current user’s account must have either the Prosumer role or MDC role in order to use the service.
- Parameters:
account_roles – The possible roles.
- flexmeasures.auth.decorators.account_roles_required(*account_roles)
Decorator which specifies that a user’s account must have all the specified roles. Example:
@app.route('/dashboard') @account_roles_required('Prosumer', 'App-subscriber') def dashboard(): return 'Dashboard'
The current user’s account must have both the Prosumer role and App-subscriber role in order to view the page.
- Parameters:
roles – The required roles.
- flexmeasures.auth.decorators.permission_required_for_context(permission: str, ctx_arg_pos: int | None = None, ctx_arg_name: str | None = None, ctx_loader: Callable | None = None, pass_ctx_to_loader: bool = False)
This decorator can be used to make sure that the current user has the necessary permission to access the context. The permission needs to be a known permission and is checked with principal descriptions from the context’s access control list (see AuthModelMixin.__acl__). This decorator will first load the context (see below for details) and then call check_access to make sure the current user has the permission.
A 403 response is raised if there is no principal for the required permission. A 401 response is raised if the user is not authenticated at all.
We will now explain how to load a context, and give an example:
The context needs to be an AuthModelMixin and is found … - by loading it via the ctx_loader callable; - otherwise:
by the keyword argument ctx_arg_name;
and/or by a position in the non-keyword arguments (ctx_arg_pos).
If nothing is passed, the context lookup defaults to ctx_arg_pos=0.
Let’s look at an example. Usually, you’d place a marshmallow field further up in the decorator chain, e.g.:
@app.route(“/resource/<resource_id>”, methods=[“GET”]) @use_kwargs(
{“the_resource”: ResourceIdField(data_key=”resource_id”)}, location=”path”,
) @permission_required_for_context(“read”, ctx_arg_name=”the_resource”) @as_json def view(resource_id: int, the_resource: Resource):
return dict(name=the_resource.name)
Note that in this example, ResourceIdField._deserialize() turns the id parameter into a Resource context (if possible).
The ctx_loader:
The ctx_loader can be a function without arguments or it takes the context loaded from the arguments as input (using pass_ctx_to_loader=True). A special case is useful when the arguments contain the context ID (not the instance). Then, the loader can be a subclass of AuthModelMixin, and this decorator will look up the instance.
Using both arg name and position:
Using both ctx_arg_name and ctx_arg_pos arguments is useful when Marshmallow de-serializes to a dict and you are using use_args. In this case, the context lookup applies first ctx_arg_pos, then ctx_arg_name.
Let’s look at a slightly more complex example where we combine both special cases from above. We parse a dictionary from the input with a Marshmallow schema, in which a context ID can be found which we need to instantiate:
@app.route(“/resource”, methods=[“POST”]) @use_args(resource_schema) @permission_required_for_context(
“create-children”, ctx_arg_pos=1, ctx_arg_name=”resource_id”, ctx_loader=Resource, pass_ctx_to_loader=True
) def post(self, resource_data: dict):
Note that in this example, resource_data is the input parsed by resource_schema, “resource_id” is one of the parameters in this schema, and Resource is a subclass of AuthModelMixin.
- flexmeasures.auth.decorators.roles_accepted(*roles)
As in Flask-Security, but also accept admin
- flexmeasures.auth.decorators.roles_required(*roles)
As in Flask-Security, but wave through if user is admin