mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-30 08:32:26 +00:00
Documentation des décorateurs d'ACL
This commit is contained in:
parent
586321fd8a
commit
84a901e3fc
1 changed files with 98 additions and 26 deletions
124
re2o/acl.py
124
re2o/acl.py
|
@ -37,24 +37,89 @@ from django.urls import reverse
|
|||
|
||||
|
||||
def acl_base_decorator(method_name, *targets, **kwargs):
|
||||
"""Base decorator for acl. It checks if the user has the permission by
|
||||
calling model.method_name. If the flag on_instance is True, tries to get an
|
||||
instance of the model by calling model.get_instance(*args, **kwargs) and
|
||||
runs instance.mehod_name rather than model.method_name.
|
||||
"""Base decorator for acl. It checks if the `request.user` has the
|
||||
permission by calling model.method_name. If the flag on_instance is True,
|
||||
tries to get an instance of the model by calling
|
||||
`model.get_instance(*args, **kwargs)` and runs `instance.mehod_name`
|
||||
rather than model.method_name.
|
||||
|
||||
It is not intended to be used as is. It is a base for others ACL
|
||||
decorators.
|
||||
|
||||
Args:
|
||||
method_name: The name of the method which is to to be used for ACL.
|
||||
(ex: 'can_edit') WARNING: if no method called 'method_name' exists,
|
||||
then no error will be triggered, the decorator will act as if
|
||||
permission was granted. This is to allow you to run ACL tests on
|
||||
fields only. If the method exists, it has to return a 2-tuple
|
||||
`(can, reason)` with `can` being a boolean stating whether the
|
||||
access is granted and `reason` a message to be displayed if `can`
|
||||
equals `False` (can be `None`)
|
||||
*targets: The targets. Targets are specified like a sequence of models
|
||||
and fields names. As an example
|
||||
```
|
||||
acl_base_decorator('can_edit', ModelA, 'field1', 'field2', \
|
||||
ModelB, ModelC, 'field3', on_instance=False)
|
||||
```
|
||||
will make the following calls (where `user` is the current user,
|
||||
`*args` and `**kwargs` are the arguments initially passed to the
|
||||
view):
|
||||
- `ModelA.can_edit(user, *args, **kwargs)`
|
||||
- `ModelA.can_change_field1(user, *args, **kwargs)`
|
||||
- `ModelA.can_change_field2(user, *args, **kwargs)`
|
||||
- `ModelB.can_edit(user, *args, **kwargs)`
|
||||
- `ModelC.can_edit(user, *args, **kwargs)`
|
||||
- `ModelC.can_change_field3(user, *args, **kwargs)`
|
||||
|
||||
Note that
|
||||
```
|
||||
acl_base_decorator('can_edit', 'field1', ModelA, 'field2', \
|
||||
on_instance=False)
|
||||
```
|
||||
would have the same effect that
|
||||
```
|
||||
acl_base_decorator('can_edit', ModelA, 'field1', 'field2', \
|
||||
on_instance=False)
|
||||
```
|
||||
But don't do that, it's silly.
|
||||
**kwargs: There is only one keyword argument, `on_instance`, which
|
||||
default value is `True`. When `on_instance` equals `False`, the
|
||||
decorator runs the ACL method on the model class rather than on
|
||||
an instance. If an instance need to fetched, it is done calling the
|
||||
assumed existing method `get_instance` of the model, with the
|
||||
arguments originally passed to the view.
|
||||
|
||||
Returns:
|
||||
The user is either redirected to their own page with an explanation
|
||||
message if at least one access is not granted, or to the view. In order
|
||||
to avoid duplicate DB calls, when the `on_instance` flag equals `True`,
|
||||
the instances are passed to the view. Example, with this decorator:
|
||||
```
|
||||
acl_base_decorator('can_edit', ModelA, 'field1', 'field2', ModelB,\
|
||||
ModelC)
|
||||
```
|
||||
The view will be called like this:
|
||||
```
|
||||
view(request, instance_of_A, instance_of_b, *args, **kwargs)
|
||||
```
|
||||
where `*args` and `**kwargs` are the original view arguments.
|
||||
"""
|
||||
on_instance = kwargs.get('on_instance', True)
|
||||
|
||||
def group_targets():
|
||||
"""This generator parses the targets of the decorator, yielding
|
||||
2-tuples of (model, [fields]).
|
||||
"""
|
||||
current_target = None
|
||||
current_fields = []
|
||||
for t in targets:
|
||||
if isinstance(t, type) and issubclass(t, Model):
|
||||
for target in targets:
|
||||
if isinstance(target, type) and issubclass(target, Model):
|
||||
if current_target:
|
||||
yield (current_target, current_fields)
|
||||
current_target = t
|
||||
current_target = target
|
||||
current_fields = []
|
||||
else:
|
||||
current_fields.append(t)
|
||||
current_fields.append(target)
|
||||
yield (current_target, current_fields)
|
||||
|
||||
def decorator(view):
|
||||
|
@ -65,6 +130,11 @@ def acl_base_decorator(method_name, *targets, **kwargs):
|
|||
instances = []
|
||||
|
||||
def process_target(target, fields):
|
||||
"""This function calls the methods on the target and checks for
|
||||
the can_change_`field` method with the given fields. It also
|
||||
stores the instances of models in order to avoid duplicate DB
|
||||
calls for the view.
|
||||
"""
|
||||
if on_instance:
|
||||
try:
|
||||
target = target.get_instance(*args, **kwargs)
|
||||
|
@ -97,37 +167,37 @@ def acl_base_decorator(method_name, *targets, **kwargs):
|
|||
|
||||
|
||||
def can_create(*models):
|
||||
"""Decorator to check if an user can create a model.
|
||||
It assumes that a valid user exists in the request and that the model has a
|
||||
method can_create(user) which returns true if the user can create this kind
|
||||
of models.
|
||||
"""Decorator to check if an user can create the given models. It runs
|
||||
`acl_base_decorator` with the flag `on_instance=False` and the method
|
||||
'can_create'. See `acl_base_decorator` documentation for further details.
|
||||
"""
|
||||
return acl_base_decorator('can_create', *models, on_instance=False)
|
||||
|
||||
|
||||
def can_edit(*targets):
|
||||
"""Decorator to check if an user can edit a model.
|
||||
It tries to get an instance of the model, using
|
||||
`model.get_instance(*args, **kwargs)` and assumes that the model has a
|
||||
method `can_edit(user)` which returns `true` if the user can edit this
|
||||
kind of models.
|
||||
"""Decorator to check if an user can edit the models.
|
||||
It runs `acl_base_decorator` with the flag `on_instance=True` and the
|
||||
method 'can_edit'. See `acl_base_decorator` documentation for further
|
||||
details.
|
||||
"""
|
||||
return acl_base_decorator('can_edit', *targets)
|
||||
|
||||
|
||||
def can_change(*targets):
|
||||
"""Decorator to check if an user can edit a field of a model class.
|
||||
Difference with can_edit : take a class and not an instance
|
||||
Difference with can_edit : takes a class and not an instance
|
||||
It runs `acl_base_decorator` with the flag `on_instance=False` and the
|
||||
method 'can_change'. See `acl_base_decorator` documentation for further
|
||||
details.
|
||||
"""
|
||||
return acl_base_decorator('can_change', *targets)
|
||||
|
||||
|
||||
def can_delete(*targets):
|
||||
"""Decorator to check if an user can delete a model.
|
||||
It tries to get an instance of the model, using
|
||||
`model.get_instance(*args, **kwargs)` and assumes that the model has a
|
||||
method `can_delete(user)` which returns `true` if the user can delete this
|
||||
kind of models.
|
||||
It runs `acl_base_decorator` with the flag `on_instance=True` and the
|
||||
method 'can_edit'. See `acl_base_decorator` documentation for further
|
||||
details.
|
||||
"""
|
||||
return acl_base_decorator('can_delete', *targets)
|
||||
|
||||
|
@ -162,16 +232,18 @@ def can_delete_set(model):
|
|||
|
||||
def can_view(*targets):
|
||||
"""Decorator to check if an user can view a model.
|
||||
It tries to get an instance of the model, using
|
||||
`model.get_instance(*args, **kwargs)` and assumes that the model has a
|
||||
method `can_view(user)` which returns `true` if the user can view this
|
||||
kind of models.
|
||||
It runs `acl_base_decorator` with the flag `on_instance=True` and the
|
||||
method 'can_view'. See `acl_base_decorator` documentation for further
|
||||
details.
|
||||
"""
|
||||
return acl_base_decorator('can_view', *targets)
|
||||
|
||||
|
||||
def can_view_all(*targets):
|
||||
"""Decorator to check if an user can view a class of model.
|
||||
It runs `acl_base_decorator` with the flag `on_instance=False` and the
|
||||
method 'can_view_all'. See `acl_base_decorator` documentation for further
|
||||
details.
|
||||
"""
|
||||
return acl_base_decorator('can_view_all', *targets, on_instance=False)
|
||||
|
||||
|
|
Loading…
Reference in a new issue