Django permissions required at Nodes and Mutations

This commit is contained in:
Carlos Martinez 2017-03-05 11:59:21 -05:00
parent 335339c7a8
commit f32a4a33db
4 changed files with 321 additions and 0 deletions

View File

@ -147,3 +147,85 @@ After this, you can use the new ``PrivateGraphQLView`` in ``urls.py``:
]
.. _LoginRequiredMixin: https://docs.djangoproject.com/en/1.10/topics/auth/default/#the-loginrequired-mixin
Adding permissions to Nodes
---------------------------
If you want to user the auth django permissions to access a node, we need to inheritance
from ``AuthNodeMixin`` and define a required permissions in the node. This will return
a ``PermissionDenied`` is the user does not have the required permissions.
.. code:: python
from graphene_django.types import DjangoObjectType
from graphene_django.auth.mixins import AuthNodeMixin
from .models import Post
class PostNode(AuthNodeMixin, DjangoObjectType):
_permission = 'app.add_post'
class Meta:
model = Post
only_fields = ('title', 'content')
interfaces = (relay.Node, )
We can set multiple required permissions like this:
.. code:: python
from graphene_django.types import DjangoObjectType
from graphene_django.auth.mixins import AuthNodeMixin
from .models import Post
class PostNode(AuthNodeMixin, DjangoObjectType):
_permission = ('app.add_post', 'app.delete_post',)
class Meta:
model = Post
only_fields = ('title', 'content')
interfaces = (relay.Node, )
Adding permissions to Mutations
---------------------------
If you want to user the auth django permissions to execute a mutation, we need to inheritance
from ``AuthMutationMixin`` and define a required permissions in the node. This will return
a ``PermissionDenied`` is the user does not have the required permissions.
.. code:: python
class CreatePet(AuthMutationMixin, graphene.Mutation):
_permission = 'app.create_pet'
pet = graphene.Field(PetNode)
class Input:
name = graphene.String(required=True)
@classmethod
def mutate(cls, root, input, context, info):
# Auth Required Virification
if cls.has_permision(context) is not True:
return cls.has_permision(context)
# End Auth
pet_name = input.get('name')
pet = Pet.objects.create(name=pet_name)
return CreatePet(pet=pet)
We can set multiple required permissions like this:
.. code:: python
class CreatePet(AuthMutationMixin, graphene.Mutation):
_permission = ('app.add_pet', 'app.delete_pet')
pet = graphene.Field(PetNode)
class Input:
name = graphene.String(required=True)
@classmethod
def mutate(cls, root, input, context, info):
# Auth Required Virification
if cls.has_permision(context) is not True:
return cls.has_permision(context)
# End Auth
pet_name = input.get('name')
pet = Pet.objects.create(name=pet_name)
return CreatePet(pet=pet)

View File

View File

@ -0,0 +1,64 @@
from django.core.exceptions import PermissionDenied
class AuthNodeMixin():
_permission = ''
@classmethod
def get_node(cls, id, context, info):
def has_perm(object_instance):
if context is None:
return PermissionDenied('Permission Denied')
if type(context) is dict:
user = context.get('user', None)
if user is None:
return PermissionDenied('Permission Denied')
else:
user = context.user
if user.is_authenticated() is False:
return PermissionDenied('Permission Denied')
if type(cls._permission) is tuple:
for permission in cls._permission:
if not user.has_perm(permission):
return False
if type(cls._permission) is str:
if not user.has_perm(cls._permission):
return False
return True
try:
object_instance = cls._meta.model.objects.get(id=id)
except cls._meta.model.DoesNotExist:
return None
if has_perm(object_instance):
return object_instance
return PermissionDenied('Permission Denied')
class AuthMutationMixin():
_permission = ''
@classmethod
def has_permision(cls, context):
user = None
if type(context) is dict:
user = context.get('user', None)
if user is None:
return PermissionDenied('Permission Denied')
else:
user = context.user
if user.is_authenticated() is False:
return PermissionDenied('Permission Denied')
if type(cls._permission) is tuple:
for permission in cls._permission:
if not user.has_perm(permission):
return PermissionDenied('Permission Denied')
return True
if type(cls._permission) is str:
if user.has_perm(cls._permission):
return True
return PermissionDenied('Permission Denied')

View File

@ -0,0 +1,175 @@
import graphene
from graphene import Schema, relay, ObjectType
from ..filter import DjangoFilterConnectionField
from django.test import TestCase, RequestFactory
from ..types import DjangoObjectType
from .models import Pet
from ..auth.mixins import AuthNodeMixin, AuthMutationMixin
class PetNode(AuthNodeMixin, DjangoObjectType):
_permission = 'app.view_pet'
class Meta:
model = Pet
interfaces = (relay.Node, )
class CreatePet(AuthMutationMixin, graphene.Mutation):
"""
Mutation for create user
example mutation:
mutation {
createPet(name: "Mila") {
pet {
id
name
}
}
}
"""
_permission = 'app.create_pet'
pet = graphene.Field(PetNode)
class Input:
name = graphene.String(required=True)
@classmethod
def mutate(cls, root, input, context, info):
if cls.has_permision(context) is not True:
return cls.has_permision(context)
pet_name = input.get('name')
pet = Pet.objects.create(name=pet_name)
return CreatePet(pet=pet)
class QueryRoot(ObjectType):
pet = relay.Node.Field(PetNode)
pets = DjangoFilterConnectionField(PetNode)
class MutationRoot(ObjectType):
create_pet = CreatePet.Field()
schema = Schema(query=QueryRoot, mutation=MutationRoot)
class MockUserContext(object):
def __init__(self, authenticated=True, is_staff=False, superuser=False, perms=()):
self.user = self
self.authenticated = authenticated
self.is_staff = is_staff
self.is_superuser = superuser
self.perms = perms
def is_authenticated(self):
return self.authenticated
def has_perm(self, check_perms):
if check_perms not in self.perms:
return False
return True
class AuthorizationTests(TestCase):
"""
This TestCase auth.
"""
@classmethod
def setUpClass(cls):
super(AuthorizationTests, cls).setUpClass()
cls.schema = schema
cls.query_mutation = '''
mutation {{
createPet(name: "{name}") {{
pet{{
id
name
}}
}}
}}
'''
cls.query_node = '''
query {
pet(id: "UGV0Tm9kZTox"){
id
name
}
}
'''
def setUp(self):
self.factory = RequestFactory()
pet_names = ['Mila', 'Kira']
for name in pet_names:
Pet.objects.create(name=name)
self.anonymous = MockUserContext(
authenticated=False
)
self.luke = MockUserContext(
authenticated=True,
perms=('app.view_pet', 'app.create_pet',)
)
self.anakin = MockUserContext(
authenticated=True,
perms=('app.view_pet',)
)
self.storm_tropper = MockUserContext(
authenticated=True,
perms=()
)
def test_mutation_anonymous(self):
"""
Making mutation with anonymous user
"""
print(self.luke.user)
result = self.schema.execute(self.query_mutation.format(name='Mila'), context_value={'user': self.anonymous})
self.assertNotEqual(result.errors, [])
self.assertEqual(result.errors[0].message, 'Permission Denied')
def test_mutation_non_permission(self):
"""
Making mutation with an user who does not have the permission
"""
result = self.schema.execute(self.query_mutation.format(name='Mila'), context_value={'user': self.anakin})
self.assertNotEqual(result.errors, [])
self.assertEqual(result.errors[0].message, 'Permission Denied')
def test_mutation_has_permission(self):
"""
Making mutation with an user who has the permission
"""
result = self.schema.execute(self.query_mutation.format(name='Mila'), context_value={'user': self.luke})
self.assertEqual(result.errors, [])
def test_query_anonymous(self):
"""
Making query with anonymous user
"""
result = self.schema.execute(self.query_node, context_value={'user': self.anonymous})
print(result.errors)
print(result.data)
self.assertNotEqual(result.errors, [])
self.assertEqual(result.errors[0].message, 'Permission Denied')
def test_query_non_permission(self):
"""
Making query with an user who does not have the permission
"""
result = self.schema.execute(self.query_node, context_value={'user': self.storm_tropper})
print(result.errors)
print(result.data)
self.assertNotEqual(result.errors, [])
self.assertEqual(result.errors[0].message, 'Permission Denied')
def test_query_has_permission(self):
"""
Making query with an user who has the permission
"""
result = self.schema.execute(self.query_node, context_value={'user': self.luke})
print(result.errors)
print(result.data)
self.assertEqual(result.errors, [])