mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-26 06: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):
|
def acl_base_decorator(method_name, *targets, **kwargs):
|
||||||
"""Base decorator for acl. It checks if the user has the permission by
|
"""Base decorator for acl. It checks if the `request.user` has the
|
||||||
calling model.method_name. If the flag on_instance is True, tries to get an
|
permission by calling model.method_name. If the flag on_instance is True,
|
||||||
instance of the model by calling model.get_instance(*args, **kwargs) and
|
tries to get an instance of the model by calling
|
||||||
runs instance.mehod_name rather than model.method_name.
|
`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)
|
on_instance = kwargs.get('on_instance', True)
|
||||||
|
|
||||||
def group_targets():
|
def group_targets():
|
||||||
|
"""This generator parses the targets of the decorator, yielding
|
||||||
|
2-tuples of (model, [fields]).
|
||||||
|
"""
|
||||||
current_target = None
|
current_target = None
|
||||||
current_fields = []
|
current_fields = []
|
||||||
for t in targets:
|
for target in targets:
|
||||||
if isinstance(t, type) and issubclass(t, Model):
|
if isinstance(target, type) and issubclass(target, Model):
|
||||||
if current_target:
|
if current_target:
|
||||||
yield (current_target, current_fields)
|
yield (current_target, current_fields)
|
||||||
current_target = t
|
current_target = target
|
||||||
current_fields = []
|
current_fields = []
|
||||||
else:
|
else:
|
||||||
current_fields.append(t)
|
current_fields.append(target)
|
||||||
yield (current_target, current_fields)
|
yield (current_target, current_fields)
|
||||||
|
|
||||||
def decorator(view):
|
def decorator(view):
|
||||||
|
@ -65,6 +130,11 @@ def acl_base_decorator(method_name, *targets, **kwargs):
|
||||||
instances = []
|
instances = []
|
||||||
|
|
||||||
def process_target(target, fields):
|
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:
|
if on_instance:
|
||||||
try:
|
try:
|
||||||
target = target.get_instance(*args, **kwargs)
|
target = target.get_instance(*args, **kwargs)
|
||||||
|
@ -97,37 +167,37 @@ def acl_base_decorator(method_name, *targets, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def can_create(*models):
|
def can_create(*models):
|
||||||
"""Decorator to check if an user can create a model.
|
"""Decorator to check if an user can create the given models. It runs
|
||||||
It assumes that a valid user exists in the request and that the model has a
|
`acl_base_decorator` with the flag `on_instance=False` and the method
|
||||||
method can_create(user) which returns true if the user can create this kind
|
'can_create'. See `acl_base_decorator` documentation for further details.
|
||||||
of models.
|
|
||||||
"""
|
"""
|
||||||
return acl_base_decorator('can_create', *models, on_instance=False)
|
return acl_base_decorator('can_create', *models, on_instance=False)
|
||||||
|
|
||||||
|
|
||||||
def can_edit(*targets):
|
def can_edit(*targets):
|
||||||
"""Decorator to check if an user can edit a model.
|
"""Decorator to check if an user can edit the models.
|
||||||
It tries to get an instance of the model, using
|
It runs `acl_base_decorator` with the flag `on_instance=True` and the
|
||||||
`model.get_instance(*args, **kwargs)` and assumes that the model has a
|
method 'can_edit'. See `acl_base_decorator` documentation for further
|
||||||
method `can_edit(user)` which returns `true` if the user can edit this
|
details.
|
||||||
kind of models.
|
|
||||||
"""
|
"""
|
||||||
return acl_base_decorator('can_edit', *targets)
|
return acl_base_decorator('can_edit', *targets)
|
||||||
|
|
||||||
|
|
||||||
def can_change(*targets):
|
def can_change(*targets):
|
||||||
"""Decorator to check if an user can edit a field of a model class.
|
"""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)
|
return acl_base_decorator('can_change', *targets)
|
||||||
|
|
||||||
|
|
||||||
def can_delete(*targets):
|
def can_delete(*targets):
|
||||||
"""Decorator to check if an user can delete a model.
|
"""Decorator to check if an user can delete a model.
|
||||||
It tries to get an instance of the model, using
|
It runs `acl_base_decorator` with the flag `on_instance=True` and the
|
||||||
`model.get_instance(*args, **kwargs)` and assumes that the model has a
|
method 'can_edit'. See `acl_base_decorator` documentation for further
|
||||||
method `can_delete(user)` which returns `true` if the user can delete this
|
details.
|
||||||
kind of models.
|
|
||||||
"""
|
"""
|
||||||
return acl_base_decorator('can_delete', *targets)
|
return acl_base_decorator('can_delete', *targets)
|
||||||
|
|
||||||
|
@ -162,16 +232,18 @@ def can_delete_set(model):
|
||||||
|
|
||||||
def can_view(*targets):
|
def can_view(*targets):
|
||||||
"""Decorator to check if an user can view a model.
|
"""Decorator to check if an user can view a model.
|
||||||
It tries to get an instance of the model, using
|
It runs `acl_base_decorator` with the flag `on_instance=True` and the
|
||||||
`model.get_instance(*args, **kwargs)` and assumes that the model has a
|
method 'can_view'. See `acl_base_decorator` documentation for further
|
||||||
method `can_view(user)` which returns `true` if the user can view this
|
details.
|
||||||
kind of models.
|
|
||||||
"""
|
"""
|
||||||
return acl_base_decorator('can_view', *targets)
|
return acl_base_decorator('can_view', *targets)
|
||||||
|
|
||||||
|
|
||||||
def can_view_all(*targets):
|
def can_view_all(*targets):
|
||||||
"""Decorator to check if an user can view a class of model.
|
"""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)
|
return acl_base_decorator('can_view_all', *targets, on_instance=False)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue