forked from graphql-python/graphene-django
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix graphql-python#79 add decorator has_perms for nodes
- Loading branch information
1 parent
5d6c7f2
commit 320d953
Showing
3 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |