Skip to main content

Role-based permissions for Django REST Framework and vanilla Django.

Project description

REST Framework Roles

rest-framework-roles

Role-based permissions for Django and Django REST Framework (functions or classes).

  • Data-driven declarative permissions for humans.
  • Full decoupling from views and models & implementation agnostic.
  • View redirection simply works.
  • Easy gradual integration with existing projects.
  • Easy optimization with decorators.

Install

Install

pip install rest-framework-roles

settings.py

INSTALLED_APPS = {
    ..
    'rest_framework',
    'rest_framework_roles',  # Must be after rest_framework
}

REST_FRAMEWORK_ROLES = {
  'roles': 'myproject.roles.ROLES',
}

REST_FRAMEWORK = {
  ..
  'permission_classes': [],  # This ensures that by default noone is allowed access
  ..
}

roles.py

from rest_framework_roles.roles import is_user, is_anon, is_admin


ROLES = {
    'admin': is_admin,
    'user': is_user,
    'anon': is_anon,
}

You can create your own role checkers for custom roles. Each checker is a simple function that takes request and view as arguments.

REST Framework example

Permissions can be set either with the decorators @allowed, @disallowed or view_permissions. Permissions are checked for all matching roles at runtime. In case of no matching role, REST Framework's permission_classes is used as fallback.

Note that views need to explictly be set any permissions either via decorators or view_permissions. Otherwise those views will not go through the permission checking. This is intended behaviour since it allows easier gradual integration when other 3rd party libraries are used.

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_roles.permissions import is_self


class UserViewSet(ModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.all()

    view_permissions = {
        'retrieve': {'user': is_self, 'admin': True},
        'create': {'anon': True},
        'list': {'admin': True},
    }

    @allowed('admin', 'user')
    @action(detail=False, methods=['get'])
    def me(self, request):
        self.kwargs['pk'] = request.user.pk
        return self.retrieve(request)

The permissions for each view are checked in order. All matching roles are checked, until permission is granted for that role.

As an example in retrieve an admin user matches both roles ('user' and 'admin'). However when trying to retrieve another user's info, the first rule does not grant access so the checking will continue. On the second rule, permission is granted and the checking ends there.

Advanced example

Sometimes you want to deal with more complex scenarios. Such refinement otherwise hairy with permission_classes, becomes much simpler.

In the example below we have many more advanced scenarios including forbidding the user to update their email.

views.py

from rest_framework_roles.permissions import is_self
from rest_framework_roles import roles


def not_updating_email(request, view):
    return 'email' not in request.data


class UserViewSet(ModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.all()
    view_permissions = {
        'retrieve': {'user': is_self, 'admin': True},
        'update': {
          'user': (is_self, not_updating_email),  # User can update everything but their email
          'admin': True,
        },
        'create': {'anon': True},W
        'list': {'admin': True},
        'me': {'user': True},
    }

    @action(detail=False, methods=['get', 'patch'])
    def me(self, request):
        self.kwargs['pk'] = request.user.pk
        if request.method == 'GET':
            return self.retrieve(request)
        elif request.method == 'PATCH':
            return self.partial_update(request)

Advanced roles

By default you get some role-checking functions for common roles like 'admin', 'user' and 'anon'. Many times though, you'll have much more roles and certain roles can be expensive to calculate.

We can easily mark the role-checking functions with a cost. The lower cost roles are checked first and then the expensive ones later. The cost is an arbitrary number so this refinement can be as deep as you wish.

from rest_framework_roles.decorators import role_checker


@role_checker(cost=0)
def is_freebie_user(request, view):
    return request.user.is_authenticated and request.user.plan == 'freebie'


@role_checker(cost=0)
def is_payed_user(request, view):
    return request.user.is_authenticated and not request.user.plan


@role_checker(cost=50)
def is_creator(request, view):
    obj = view.get_object()
    if hasattr(obj, 'creator'):
        return request.user == obj.creator
    return False

This is a bit similar to Django REST's check_permissions and check_object_permissions which in this case would translate to a max of 2 different costs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rest_framework_roles-0.3.1.tar.gz (11.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rest_framework_roles-0.3.1-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

File details

Details for the file rest_framework_roles-0.3.1.tar.gz.

File metadata

  • Download URL: rest_framework_roles-0.3.1.tar.gz
  • Upload date:
  • Size: 11.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.2.0 requests-toolbelt/0.8.0 tqdm/4.43.0 CPython/3.8.2

File hashes

Hashes for rest_framework_roles-0.3.1.tar.gz
Algorithm Hash digest
SHA256 4a01ccf906a49552ba489b70a7d9fa5344e08bc6dbbcffdcf6963598fc5627ca
MD5 22cf88736ed85efb0993c107f43ab446
BLAKE2b-256 137855ba5c4b9a17dadbfce3f5423b5399de8a1b1d194be7ae81a69d8ac56234

See more details on using hashes here.

File details

Details for the file rest_framework_roles-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: rest_framework_roles-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 12.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.2.0 requests-toolbelt/0.8.0 tqdm/4.43.0 CPython/3.8.2

File hashes

Hashes for rest_framework_roles-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5d5dab5ab102ef4b1010bb3c2b0cd4cfdd56186ad7bef5d9ad91a6b5c2898339
MD5 b1e304db50b1c9eba34523443b4d2816
BLAKE2b-256 67e36d979d259d35f7b56759f653936a05554771a4e047ba4cdf3f0c54211baa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page