|
|
|
# DjangoLDP Permissions
|
|
|
|
|
|
|
|
DjangoLDP has a permissions system which differs slightly from Django's inbuilt permissions, although it is built on it
|
|
|
|
DjangoLDP provides some permissions classes built on top of [Rest Framework permissions classes](https://www.django-rest-framework.org/api-guide/permissions/)
|
|
|
|
|
|
|
|
The built-in permissions are defined in the DjangoLDP Model.Meta:
|
|
|
|
```python
|
|
|
|
default_permissions = ('add', 'change', 'delete', 'view', 'control')
|
|
|
|
```
|
|
|
|
If you define your own Meta class, these permissions will not be inherited by default, [so you should inherit them explicitly](https://docs.djangoproject.com/fr/2.2/topics/db/models/#meta-inheritance)
|
|
|
|
|
|
|
|
# DjangoLDP >= 2.1
|
|
|
|
|
|
|
|
## LDPBasePermissions
|
|
|
|
|
|
|
|
Built on Rest Framework's `DjangoObjectPermissions`, this is the class which any custom permissions should extend, as it defines the custom logic necessary for DjangoLDP
|
|
|
|
|
|
|
|
Very generally in Django Rest Framework you have two key functions to override:
|
|
|
|
* `has_permission` returns `True` or `False` depending on whether the passed request should be given access to the passed _view_. It is therefore the _view-level_ permission
|
|
|
|
* `has_object_permission` is similar, but passing an object it returns a boolean based on the _object_ level permissions
|
|
|
|
|
|
|
|
LDPPermissions do a little more than the Rest Framework:
|
|
|
|
* we extend a new concept of `has_model_permission`, which defines whether a given request has permission to access the object based on _model_ permissions
|
|
|
|
* we _output_ lists of permissions granted, which we use to serialize the permissions on each object/container, as part of the LDP standards
|
|
|
|
|
|
|
|
In order to provide the output functionality, there are three key functions provided by `LDPBasePermissions`:
|
|
|
|
* `get_model_permissions(self, request, view, obj=None)`
|
|
|
|
* `get_object_permissions(self, request, view, obj)`
|
|
|
|
* `get_user_permissions(self, request, view, obj=None)`
|
|
|
|
|
|
|
|
All 3 return a `set` of permissions. Overriding these functions allows the permissions to be serialized on the models, but if this is for whatever reason disabled in your app then you can fallback onto the `has_permission` and `has_object_permission` provided by DRF
|
|
|
|
|
|
|
|
In fact it's very rare that you should need to override `has_object_permission` or `has_model_permission` at all, as these by default analyse and return the results of their counterparts
|
|
|
|
|
|
|
|
To provide custom permissions, simply extend the `LDPBasePermissions` or one or more of the subclasses below, and then set the `permission_classes` to your custom models and/or views:
|
|
|
|
|
|
|
|
```python
|
|
|
|
class MyModel(Model):
|
|
|
|
...
|
|
|
|
|
|
|
|
class Meta(Model.Meta):
|
|
|
|
permission_classes = [MyPermissions]
|
|
|
|
```
|
|
|
|
|
|
|
|
### perms_map
|
|
|
|
|
|
|
|
Inherited from `DjangoObjectPermissions`, the `perms_map` in the `LDPBasePermissions` class defines the required permissions for a given action and is by default:
|
|
|
|
|
|
|
|
```python
|
|
|
|
perms_map = {
|
|
|
|
'GET': ['%(app_label)s.view_%(model_name)s'],
|
|
|
|
'OPTIONS': [],
|
|
|
|
'HEAD': ['%(app_label)s.view_%(model_name)s'],
|
|
|
|
'POST': ['%(app_label)s.add_%(model_name)s'],
|
|
|
|
'PUT': ['%(app_label)s.change_%(model_name)s'],
|
|
|
|
'PATCH': ['%(app_label)s.change_%(model_name)s'],
|
|
|
|
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## OwnerAuthAnonPermissions
|
|
|
|
|
|
|
|
Extends `LDPBasePermissions` to provide model-level permissions for three user roles: `anonymous`, `authenticated` and `owner`
|
|
|
|
|
|
|
|
`djangoldp.models.Model.is_owner` defines whether or not I am an owner of a model (and can be overridden), but by default this means that the `request.user` matches to the user defined on the model attribute, set by the `owner_field` [on the model's Meta](https://git.startinblox.com/djangoldp-packages/djangoldp#anonymous_perms-user_perms-owner_perms)
|
|
|
|
|
|
|
|
## LDPObjectLevelPermissions
|
|
|
|
|
|
|
|
Extends `LDPBasePermissions` to provide object-level permissions, based on all `AuthenticationBackends` configured in your application (this will likely be Django's `Groups` Django-Guardian's object-level permissions). Inherit this class' `get_object_permissions` to allow object and group level permissions in your custom class
|
|
|
|
|
|
|
|
### Django Guardian
|
|
|
|
|
|
|
|
DjangoLDP comes packaged with [Django-Guardian](https://django-guardian.readthedocs.io/en/stable/overview.html), which is used for supporting object-level permissions
|
|
|
|
|
|
|
|
DjangoLDP Models are registered with the Admin site using Django Guardian by default, but if you have created a custom Admin class for your model you will need to extend `GuardedModelAdmin` manually:
|
|
|
|
```python
|
|
|
|
from guardian.admin import GuardedModelAdmin
|
|
|
|
|
|
|
|
class CustomAdmin(GuardedModelAdmin):
|
|
|
|
pass
|
|
|
|
```
|
|
|
|
|
|
|
|
## LDPPermissions
|
|
|
|
|
|
|
|
Combines `OwnerAuthAnonPermissions` and `LDPObjectLevelPermissions` with a custom `filter_backend` and provides a cache. By default applied to `Model` and `LDPViewSet` subclasses when no custom permissions present
|
|
|
|
|
|
|
|
## FilterBackends and LDPPermissions
|
|
|
|
|
|
|
|
One key divergence from Django Rest Framework's design is in the ability to attach `filter_backends` to custom permissions classes
|
|
|
|
|
|
|
|
A key design feature of DjangoLDP is to allow ViewSet and serializer content to be automatically configured and serialized as much as possible for the general use case
|
|
|
|
|
|
|
|
By default `LDPViewSet` will iterate all of the permissions classes [configured on a DjangoLDP Model](https://git.startinblox.com/djangoldp-packages/djangoldp#permissions_classes), will search for any `filter_backends` which are set on these and apply them:
|
|
|
|
```python
|
|
|
|
# this is the whole of the LDPPermissions class
|
|
|
|
class LDPPermissions(LDPObjectLevelPermissions, OwnerAuthAnonPermissions, CachedLDPBasePermission):
|
|
|
|
filter_backends = [LDPPermissionsFilterBackend]
|
|
|
|
```
|
|
|
|
|
|
|
|
`LDPSerializer` does a similar thing (via the `LDListMixin`) for nested fields (e.g. applying the `MemberPermissions.filter_backends` to `mymodel/1/members/`)
|
|
|
|
|
|
|
|
# DjangoLDP v1
|
|
|
|
|
|
|
|
DjangoLDP permissions classes perform 3 roles:
|
|
|
|
* Control: permit or deny actions based on the user permission
|
| ... | ... | @@ -9,19 +107,13 @@ DjangoLDP permissions classes perform 3 roles: |
|
|
|
|
|
|
|
The important function for all of these roles is `user_permissions`, defined in the base class `LDPPermissions`. This function returns a list of permissions which the user has on the resource, and is used by `has_permission` and `has_object_permission` when granting or denying an action
|
|
|
|
|
|
|
|
The built-in permissions are defined in the DjangoLDP Model.Meta:
|
|
|
|
```python
|
|
|
|
default_permissions = ('add', 'change', 'delete', 'view', 'control')
|
|
|
|
```
|
|
|
|
If you define your own Meta class, these permissions will not be inherited by default, [so you should inherit them explicitly](https://docs.djangoproject.com/fr/2.2/topics/db/models/#meta-inheritance)
|
|
|
|
|
|
|
|
## Why?
|
|
|
|
|
|
|
|
Traditionally, Django provides methods such as `has_perms`, returning a boolean which returns `True` or `False` depending on whether a user has permission to perform an action. Whilst this is useful for the Filter and Control roles of our permissions class, it doesn't provide the Output role, returning which permissions are available to the user
|
|
|
|
|
|
|
|
# Extending Permissions for Your Own Models
|
|
|
|
## Extending Permissions for Your Own Models
|
|
|
|
|
|
|
|
## Anonymous, Authenticated and Owner Permissions
|
|
|
|
### Anonymous, Authenticated and Owner Permissions
|
|
|
|
|
|
|
|
As a shortcut, you can define extend your model's Meta to include a list of permissions which should be given to anonymous users, authenticated users and the owner of a resource (usually the creator)
|
|
|
|
```python
|
| ... | ... | @@ -40,7 +132,7 @@ class Meta(model.Meta): |
|
|
|
owner_field = 'user'
|
|
|
|
```
|
|
|
|
|
|
|
|
## Object-Level Permissions
|
|
|
|
### Object-Level Permissions
|
|
|
|
|
|
|
|
DjangoLDP comes packaged with [Django-Guardian](https://django-guardian.readthedocs.io/en/stable/overview.html), which is used for supporting object-level permissions
|
|
|
|
|
| ... | ... | |
| ... | ... | |