mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-02-22 22:53:05 +03:00
Django permissions required at Nodes and Mutations
This commit is contained in:
parent
335339c7a8
commit
f32a4a33db
|
@ -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
|
.. _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)
|
||||||
|
|
0
graphene_django/auth/__init__.py
Normal file
0
graphene_django/auth/__init__.py
Normal file
64
graphene_django/auth/mixins.py
Normal file
64
graphene_django/auth/mixins.py
Normal 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')
|
175
graphene_django/tests/test_auth_mixins.py
Normal file
175
graphene_django/tests/test_auth_mixins.py
Normal 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, [])
|
Loading…
Reference in New Issue
Block a user