mirror of
https://github.com/jazzband/django-admin2.git
synced 2026-04-26 17:44:45 +00:00
Adding reference documentation for permission handling.
This commit is contained in:
parent
2b268eb089
commit
65d54083ce
2 changed files with 150 additions and 23 deletions
|
|
@ -18,18 +18,45 @@ import re
|
|||
|
||||
|
||||
def is_authenticated(request, view, obj=None):
|
||||
'''
|
||||
Checks if the current user is authenticated.
|
||||
'''
|
||||
return request.user.is_authenticated()
|
||||
|
||||
|
||||
def is_staff(request, view, obj=None):
|
||||
'''
|
||||
Checks if the current user is a staff member.
|
||||
'''
|
||||
return request.user.is_staff
|
||||
|
||||
|
||||
def is_superuser(request, view, obj=None):
|
||||
'''
|
||||
Checks if the current user is a superuser.
|
||||
'''
|
||||
return request.user.is_superuser
|
||||
|
||||
|
||||
def model_permission(permission):
|
||||
'''
|
||||
This is actually a permission check factory. It means that it will return
|
||||
a function that can then act as a permission check. The returned callable
|
||||
will check if the user has the with ``permission`` provided model
|
||||
permission. You can use ``{app_label}`` and ``{model_name}`` as
|
||||
placeholders in the permission name. They will be replaced with the
|
||||
``app_label`` and the ``model_name`` (in lowercase) of the model that the
|
||||
current view is operating on.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
check_add_perm = model_permission('{app_label}.add_{model_name}')
|
||||
|
||||
class ModelAddPermission(permissions.BasePermission):
|
||||
permissions = [check_add_perm]
|
||||
'''
|
||||
def has_permission(request, view, obj=None):
|
||||
model_class = getattr(view, 'model', None)
|
||||
queryset = getattr(view, 'queryset', None)
|
||||
|
|
@ -38,19 +65,17 @@ def model_permission(permission):
|
|||
model_class = queryset.model
|
||||
|
||||
assert model_class, (
|
||||
'Cannot apply DjangoModelPermissions on a view that does not '
|
||||
'have `.model` or `.queryset` property.')
|
||||
'Cannot apply model permissions on a view that does not '
|
||||
'have a `.model` or `.queryset` property.')
|
||||
|
||||
kwargs = {
|
||||
'app_label': model_class._meta.app_label,
|
||||
'model_name': model_class._meta.module_name
|
||||
}
|
||||
permission_name = permission % kwargs
|
||||
permission_name = permission.format(
|
||||
app_label=model_class._meta.app_label,
|
||||
model_name=model_class._meta.module_name)
|
||||
return request.user.has_perm(permission_name, obj)
|
||||
return has_permission
|
||||
|
||||
|
||||
class AdminPermission(object):
|
||||
class BasePermission(object):
|
||||
'''
|
||||
Provides a base class with a common API. It implements a compatible
|
||||
interface to django-rest-framework permission backends.
|
||||
|
|
@ -79,7 +104,7 @@ class AdminPermission(object):
|
|||
return self.has_permission(request, view, obj)
|
||||
|
||||
|
||||
class IsStaffPermission(AdminPermission):
|
||||
class IsStaffPermission(BasePermission):
|
||||
'''
|
||||
It ensures that the user is authenticated and is a staff member.
|
||||
'''
|
||||
|
|
@ -88,7 +113,9 @@ class IsStaffPermission(AdminPermission):
|
|||
is_staff)
|
||||
|
||||
|
||||
class ModelPermission(AdminPermission):
|
||||
# TODO: needs documentation
|
||||
# TODO: needs integration into the REST API
|
||||
class ModelPermission(BasePermission):
|
||||
'''
|
||||
Checks if the necessary model permissions are set for the accessed object.
|
||||
'''
|
||||
|
|
@ -99,27 +126,39 @@ class ModelPermission(AdminPermission):
|
|||
'GET': (),
|
||||
'OPTIONS': (),
|
||||
'HEAD': (),
|
||||
'POST': (model_permission('%(app_label)s.add_%(model_name)s'),),
|
||||
'PUT': (model_permission('%(app_label)s.change_%(model_name)s'),),
|
||||
'PATCH': (model_permission('%(app_label)s.change_%(model_name)s'),),
|
||||
'DELETE': (model_permission('%(app_label)s.delete_%(model_name)s'),),
|
||||
'POST': (model_permission('{app_label}.add_{model_name}'),),
|
||||
'PUT': (model_permission('{app_label}.change_{model_name}'),),
|
||||
'PATCH': (model_permission('{app_label}.change_{model_name}'),),
|
||||
'DELETE': (model_permission('{app_label}.delete_{model_name}'),),
|
||||
}
|
||||
|
||||
|
||||
class ModelViewPermission(AdminPermission):
|
||||
permissions = (model_permission('%(app_label)s.view_%(model_name)s'),)
|
||||
class ModelViewPermission(BasePermission):
|
||||
'''
|
||||
Checks if the user has the ``<app>.view_<model>`` permission.
|
||||
'''
|
||||
permissions = (model_permission('{app_label}.view_{model_name}'),)
|
||||
|
||||
|
||||
class ModelAddPermission(AdminPermission):
|
||||
permissions = (model_permission('%(app_label)s.add_%(model_name)s'),)
|
||||
class ModelAddPermission(BasePermission):
|
||||
'''
|
||||
Checks if the user has the ``<app>.add_<model>`` permission.
|
||||
'''
|
||||
permissions = (model_permission('{app_label}.add_{model_name}'),)
|
||||
|
||||
|
||||
class ModelChangePermission(AdminPermission):
|
||||
permissions = (model_permission('%(app_label)s.change_%(model_name)s'),)
|
||||
class ModelChangePermission(BasePermission):
|
||||
'''
|
||||
Checks if the user has the ``<app>.change_<model>`` permission.
|
||||
'''
|
||||
permissions = (model_permission('{app_label}.change_{model_name}'),)
|
||||
|
||||
|
||||
class ModelDeletePermission(AdminPermission):
|
||||
permissions = (model_permission('%(app_label)s.delete_%(model_name)s'),)
|
||||
class ModelDeletePermission(BasePermission):
|
||||
'''
|
||||
Checks if the user has the ``<app>.delete_<model>`` permission.
|
||||
'''
|
||||
permissions = (model_permission('{app_label}.delete_{model_name}'),)
|
||||
|
||||
|
||||
class TemplatePermission(object):
|
||||
|
|
|
|||
|
|
@ -33,5 +33,93 @@ The documentation works off a simple set of models, as listed below.
|
|||
def __unicode__(self):
|
||||
return self.body
|
||||
|
||||
Permissions
|
||||
===========
|
||||
|
||||
|
||||
Permissions are handled on a per view basis. So basically each admin view can
|
||||
hold its own permission handling. That way you are very flexible in defining
|
||||
who is allowed to access your edit view without limitting yourself to simple
|
||||
model based permissions (like ``user.has_perm('blog.change_post')``.
|
||||
|
||||
You can attach a permission backend to a view by assigning a list of those to
|
||||
the ``permission_classes`` attribute:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.views import generic
|
||||
from djadmin2.viewmixins import Admin2Mixin
|
||||
from djadmin2 import permissions
|
||||
|
||||
|
||||
class MyView(Admin2Mixin, generic.TemplateView):
|
||||
permission_classes = (
|
||||
permissions.IsStaffPermission,
|
||||
permissions.ModelViewPermission)
|
||||
|
||||
See the following sections on which permission classes ship with
|
||||
django-admin2, ready to use and how you can roll your own.
|
||||
|
||||
Builtin permission backends
|
||||
---------------------------
|
||||
|
||||
.. autoclass:: djadmin2.permissions.IsStaffPermission
|
||||
|
||||
.. autoclass:: djadmin2.permissions.ModelViewPermission
|
||||
|
||||
.. autoclass:: djadmin2.permissions.ModelAddPermission
|
||||
|
||||
.. autoclass:: djadmin2.permissions.ModelChangePermission
|
||||
|
||||
.. autoclass:: djadmin2.permissions.ModelDeletePermission
|
||||
|
||||
Writing your own permission backend
|
||||
-----------------------------------
|
||||
|
||||
Internally a permission class uses so called *permission checks* to implement
|
||||
the real logic of verifying that a user has the correct permissions. A
|
||||
permission check has the real simple interface of accepting two position
|
||||
arguments and an optional third one. The first is the current ``request``,
|
||||
the second the ``view`` on which the permission check is performed against.
|
||||
The third and optional one is an arbitrary that can be passed into the
|
||||
permission checking machinery to implement object level permissions. Based on
|
||||
these arguments should the permission check than return either ``True`` if the
|
||||
permission shall be granted. A returned ``False`` means that the permission
|
||||
check failed and access to the user shall be denied.
|
||||
|
||||
Here is an example implementation of a custom permission check:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def secret_information_check(request, view, obj=None):
|
||||
'''
|
||||
Only allow superusers access to secret information.
|
||||
'''
|
||||
if 'secret' in obj.title.lower() and not request.user.is_superuser:
|
||||
return False
|
||||
return True
|
||||
|
||||
You can use the following predefined permission checks or built your own:
|
||||
|
||||
.. autofunction:: djadmin2.permissions.is_authenticated
|
||||
|
||||
.. autofunction:: djadmin2.permissions.is_staff
|
||||
|
||||
.. autofunction:: djadmin2.permissions.is_superuser
|
||||
|
||||
.. autofunction:: djadmin2.permissions.model_permission
|
||||
|
||||
You can now build your own permission class by subclassing
|
||||
``djadmin2.permissions.BasePermission`` and assigning a list of those
|
||||
permission checks to the ``permissions`` attribute:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class SecretContentPermission(permissions.BasePermission):
|
||||
permissions = (
|
||||
permissions.is_staff,
|
||||
secret_information_check)
|
||||
|
||||
Permissions in templates
|
||||
------------------------
|
||||
|
||||
TODO ...
|
||||
|
|
|
|||
Loading…
Reference in a new issue