Skip to content

Commit

Permalink
fix graphql-python#79 add decorator has_perms for nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
valdergallo committed Jan 24, 2017
1 parent 5d6c7f2 commit 320d953
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
25 changes: 25 additions & 0 deletions docs/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,28 @@ method to your ``DjangoObjectType``.
if post.published or context.user == post.owner:
return post
return None
Permission node access
----------------------

For restrict access using permissions, use the `has_perm` decorator in node.

.. code:: python
from graphene_django.types import DjangoObjectType
from graphene_django.decorator import has_perms
from .models import Post
class PostNode(DjangoObjectType):
class Meta:
model = Post
only_fields = ('title', 'content')
interfaces = (relay.Node, )
@has_perms(["django_app.django_can_see_content_permission"])
def resolve_content(self, id, context, info):
return self.content
51 changes: 51 additions & 0 deletions graphene_django/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# coding: utf-8
from functools import wraps
from django.http import HttpResponseForbidden


def has_perms(permissions):
"""
Check if have user logged and permissions to see some value
Example:
class CityNode(DjangoObjectType):
class Meta(object):
interfaces = (relay.Node,)
model = City
only_fields = (
'name', 'locality', 'slug', 'state', 'active',
)
@has_perms(["django_city_app.can_see_location"])
def resolve_location(self, args, context, info):
return self.locality.pos
Args:
permissions: ["django_app.permission_codename",]
"""
def decorator(method):
if callable(permissions):
method.permissions = []
else:
method.permissions = permissions

@wraps(method)
def wrapper(*args, **kwargs):
context = kwargs.get('context', dict(zip(method.func_code.co_varnames,
args)).get('context', None))
if not context:
return HttpResponseForbidden('Forbidden. No context, no access.')
try:
user = context.user
except AttributeError:
return HttpResponseForbidden('Forbidden. No request.')

if method.permissions and not user.has_perms(method.permissions):
return HttpResponseForbidden('Forbidden. User without access')
return method(*args, **kwargs)
return wrapper

if callable(permissions):
return decorator(permissions)

return decorator
80 changes: 80 additions & 0 deletions graphene_django/tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# encoding: utf-8
from django.utils.unittest import TestCase
from ..decorators import has_perms


class MockContext(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
self.status_code = 200

def is_authenticated(self):
return self.authenticated

def has_perms(self, check_perms):
for perm in check_perms:
if perm not in self.perms:
return False
return True


class TestHasPermsDecorator(TestCase):

@classmethod
@has_perms(['django_app.dummy_permission'])
def check_user_perms_func(cls, input, context, info=None):
cls.status_code = 200
cls.content = True
return cls

def test_get_content_without_permission(self):
context = MockContext(
authenticated=True
)
request = TestHasPermsDecorator.check_user_perms_func(None, context=context)
self.assertEqual(request.status_code, 403)
self.assertEqual(request.content, 'Forbidden. User without access')

def test_get_content_without_authentication(self):
context = MockContext(
authenticated=False
)
request = TestHasPermsDecorator.check_user_perms_func(None, context=context)
self.assertEqual(request.status_code, 403)
self.assertEqual(request.content, 'Forbidden. User is not authenticated.')

def test_get_context_with_permission(self):
context = MockContext(
authenticated=True,
perms=['django_app.dummy_permission']

)
request = TestHasPermsDecorator.check_user_perms_func(None, context=context)
self.assertEqual(request.status_code, 200)
self.assertEqual(request.content, True)

def test_get_context_with_diffent_and_valid_permission(self):
context = MockContext(
authenticated=True,
perms=['another_app.dummy_permission',
'django_app.dummy_permission']

)
request = TestHasPermsDecorator.check_user_perms_func(None, context=context)
self.assertEqual(request.status_code, 200)
self.assertEqual(request.content, True)

def test_get_context_with_diffent_and_invalid_permission(self):
context = MockContext(
authenticated=True,
perms=['another_app.dummy_permission',
'another_app.dummy_permission2']

)
request = TestHasPermsDecorator.check_user_perms_func(None, context=context)
self.assertEqual(request.status_code, 403)
self.assertEqual(request.content, 'Forbidden. User without access')

0 comments on commit 320d953

Please sign in to comment.