Skip to content

Django permissions required for Nodes, Mutations and Connections #301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions docs/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,88 @@ method to your ``DjangoObjectType``.
return post
return None

Require permissions
---------------------

If you want you can require Django permissions to access to *Nodes*,
*Mutations* and *Connections*.

Node example:

.. code:: python
from graphene_django.types import DjangoObjectType
from graphene_django.auth import node_require_permission
from .models import Reporter

class ReporterType(DjangoObjectType):

class Meta:
model = Reporter
interfaces = (Node, )

@classmethod
@node_require_permission(permissions=('can_view_report', 'can_edit_foo', ))
def get_node(cls, info, id):
return super(ReporterType, cls).get_node(info, id)

Mutation example:

.. code:: python
from rest_framework import serializers
from graphene_django.types import DjangoObjectType
from graphene_django.auth import node_require_permission
from graphene_django.rest_framework.mutation import SerializerMutation
from .models import Reporter


class ReporterSerializer(serializers.ModelSerializer):
class Meta:
model = Reporter
fields = '__all__'


class MyMutation(SerializerMutation):
class Meta:
serializer_class = ReporterSerializer

@classmethod
@mutation_require_permission(permissions=('can_view_foo', 'can_edit_foo', ))
def mutate_and_get_payload(cls, root, info, **input):
return super(MyMutation, cls).mutate_and_get_payload(root, info, **input)

Connection example:

.. code:: python
import graphene
from graphene_django.fields import DjangoConnectionField
from graphene_django.auth import connection_require_permission, node_require_permission
from graphene_django.types import DjangoObjectType
from .models import Reporter

class ReporterType(DjangoObjectType):

class Meta:
model = Reporter
interfaces = (Node, )

@classmethod
@node_require_permission(permissions=('can_view_report', 'can_edit_foo', ))
def get_node(cls, info, id):
return super(ReporterType, cls).get_node(info, id)

class MyAuthDjangoConnectionField(DjangoConnectionField):

@classmethod
@connection_require_permission(permissions=('can_view_foo', ))
def connection_resolver(cls, resolver, connection, default_manager, max_limit,
enforce_first_or_last, root, info, **args):
return super(MyAuthDjangoConnectionField, cls).connection_resolver(
resolver, connection, default_manager, max_limit,
enforce_first_or_last, root, info, **args)

class Query(graphene.ObjectType):
all_reporters = MyAuthDjangoConnectionField(ReporterType)


Adding Login Required
---------------------
Expand Down
11 changes: 11 additions & 0 deletions graphene_django/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .decorators import (
node_require_permission,
mutation_require_permission,
connection_require_permission
)

__all__ = [
'node_require_permission',
'mutation_require_permission',
'connection_require_permission'
]
44 changes: 44 additions & 0 deletions graphene_django/auth/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from functools import wraps
from django.core.exceptions import PermissionDenied
from ..fields import DjangoConnectionField

from .utils import has_perm


def node_require_permission(permissions):
def require_permission_decorator(func):
@wraps(func)
def func_wrapper(cls, info, id):
if has_perm(permissions=permissions, context=info.context):
return func(cls, info, id)
raise PermissionDenied('Permission Denied')
return func_wrapper
return require_permission_decorator


def mutation_require_permission(permissions):
def require_permission_decorator(func):
@wraps(func)
def func_wrapper(cls, root, info, **input):
if has_perm(permissions=permissions, context=info.context):
return func(cls, root, info, **input)
return cls(errors=PermissionDenied('Permission Denied'))
return func_wrapper
return require_permission_decorator


def connection_require_permission(permissions):
def require_permission_decorator(func):
@wraps(func)
def func_wrapper(
cls, resolver, connection, default_manager, max_limit,
enforce_first_or_last, root, info, **args):
if has_perm(permissions=permissions, context=info.context):
return func(
cls, resolver, connection, default_manager, max_limit,
enforce_first_or_last, root, info, **args)
return DjangoConnectionField.connection_resolver(
resolver, connection, [PermissionDenied('Permission Denied'), ], max_limit,
enforce_first_or_last, root, info, **args)
return func_wrapper
return require_permission_decorator
39 changes: 39 additions & 0 deletions graphene_django/auth/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
""""
Auth utils module.

Define some functios to authorize user to user mutations or nodes.
"""


def is_related_to_user(object_instance, user, field):
"""Return True when the object_instance is related to user."""
user_instance = getattr(object_instance, field, None)
if user:
if user_instance == user:
return True
return False


def is_authorized_to_mutate_object(model, user, id, field):
"""Return True when the when the user is unauthorized."""
object_instance = model.objects.get(pk=id)
if is_related_to_user(object_instance, user, field):
return True
return False


def has_perm(permissions, context):
"""
Validates if the user in the context has the permission required.
"""
assert permissions
if context is None:
return False
user = context.user
if user.is_authenticated() is False:
return False

for permission in permissions:
if not user.has_perm(permission):
return False
return True
Loading