Add test to permission type

This commit is contained in:
Olivia Rodriguez Valdes 2019-01-07 08:25:19 -05:00
parent 56a4ae9194
commit 106d3e063d
4 changed files with 126 additions and 9 deletions

View File

@ -164,4 +164,4 @@ class DjangoPermissionField(Field):
def get_resolver(self, parent_resolver):
"""Intercept resolver to analyse permissions"""
return partial(auth_resolver, self.resolver or parent_resolver, self.permissions, True)
return partial(auth_resolver, self.resolver or parent_resolver, self.permissions, None, None, True)

View File

@ -1,10 +1,10 @@
from mock import patch
from graphene import Interface, ObjectType, Schema, Connection, String
from graphene import Interface, ObjectType, Schema, Connection, String, Field
from graphene.relay import Node
from .. import registry
from ..types import DjangoObjectType, DjangoObjectTypeOptions
from ..types import DjangoObjectType, DjangoObjectTypeOptions, DjangoPermissionObjectType
from .models import Article as ArticleModel
from .models import Reporter as ReporterModel
@ -224,3 +224,101 @@ def test_django_objecttype_exclude_fields():
fields = list(Reporter._meta.fields.keys())
assert "email" not in fields
def extra_field_resolver(root, info, **kwargs):
return 'extra field'
class PermissionArticle(DjangoPermissionObjectType):
"""Basic Type to test"""
class Meta(object):
"""Meta Class"""
field_to_permission = {
'headline': ('content_type.permission1',),
'pub_date': ('content_type.permission2',)
}
permission_to_field = {
'content_type.permission3': ('headline', 'reporter', 'extra_field',)
}
model = ArticleModel
extra_field = Field(String, resolver=extra_field_resolver)
def resolve_headline(self, info, **kwargs):
return 'headline'
def test_django_permissions():
expected = {
'headline': ('content_type.permission1', 'content_type.permission3'),
'pub_date': ('content_type.permission2',),
'reporter': ('content_type.permission3',),
'extra_field': ('content_type.permission3',),
}
assert PermissionArticle._field_permissions == expected
def test_permission_resolver():
MyType = object()
class Viewer(object):
def has_perm(self, perm):
return perm == 'content_type.permission3'
class Info(object):
class Context(object):
user = Viewer()
context = Context()
resolved = PermissionArticle.resolve_headline(MyType, Info())
assert resolved == 'headline'
def test_resolver_without_permission():
MyType = object()
class Viewer(object):
def has_perm(self, perm):
return False
class Info(object):
class Context(object):
user = Viewer()
context = Context()
resolved = PermissionArticle.resolve_headline(MyType, Info())
assert resolved is None
def test_permission_resolver_to_field():
MyType = object()
class Viewer(object):
def has_perm(self, perm):
return perm == 'content_type.permission3'
class Info(object):
class Context(object):
user = Viewer()
context = Context()
resolved = PermissionArticle.resolve_extra_field(MyType, Info())
assert resolved == 'extra field'
def test_resolver_to_field_without_permission():
MyType = object()
class Viewer(object):
def has_perm(self, perm):
return perm != 'content_type.permission3'
class Info(object):
class Context(object):
user = Viewer()
context = Context()
resolved = PermissionArticle.resolve_extra_field(MyType, Info())
assert resolved is None

View File

@ -9,7 +9,7 @@ from graphene.types.utils import yank_fields_from_attrs
from .converter import convert_django_field_with_choices
from .registry import Registry, get_global_registry
from .utils import DJANGO_FILTER_INSTALLED, get_model_fields, is_valid_django_model
from .utils import DJANGO_FILTER_INSTALLED, get_model_fields, is_valid_django_model, auth_resolver
def construct_fields(model, registry, only_fields, exclude_fields):
@ -170,7 +170,7 @@ class DjangoPermissionObjectType(DjangoObjectType):
for field_name, field_permissions in cls._field_permissions.items():
attr = 'resolve_{}'.format(field_name)
resolver = getattr(cls, attr, None)
resolver = getattr(cls._meta.fields[field_name], 'resolver', None) or getattr(cls, attr, None)
if not hasattr(field_permissions, '__iter__'):
field_permissions = tuple(field_permissions)
@ -188,7 +188,7 @@ class DjangoPermissionObjectType(DjangoObjectType):
_as=Field,
)
for name, field in django_fields.items():
if isinstance(field._type, NonNull):
if hasattr(field, '_type') and isinstance(field._type, NonNull):
field._type = field._type._of_type
setattr(cls, name, field)
@ -211,7 +211,7 @@ class DjangoPermissionObjectType(DjangoObjectType):
cls._field_permissions[field] = cls._field_permissions.get(field, tuple()) + permissions
@classmethod
def set_auth_resolver(cls, name, permissions, field, resolver=None):
def set_auth_resolver(cls, name, permissions, resolver=None):
"""
Set middleware resolver to handle field permissions
:param name: Field name
@ -220,4 +220,4 @@ class DjangoPermissionObjectType(DjangoObjectType):
:param resolver: Field resolver
:return: Middleware resolver to check permissions
"""
field.resolver = partial(auth_resolver, field.resolver or resolver, name, permissions, None, False)
return partial(auth_resolver, resolver, permissions, name, None, False)

View File

@ -6,6 +6,7 @@ from django.db.models.manager import Manager
# from graphene.utils import LazyList
from graphene.types.resolver import get_default_resolver
from graphene.utils.get_unbound_function import get_unbound_function
@ -110,11 +111,27 @@ def resolve_bound_resolver(resolver, root, info, **args):
return resolver(root, info, **args)
def auth_resolver(parent_resolver, permissions, raise_exception, root, info, **args):
def resolve_default_resolver(attname, default_value, root, info, **args):
"""
Resolve field with default resolver
:param attname: Field name
:param default_value: Field default value
:param root: Schema root
:param info: Schema info
:param args: Schema args
:return: Resolved field
"""
resolver = get_default_resolver()
return resolver(attname, default_value, root, info, **args)
def auth_resolver(parent_resolver, permissions, attname, default_value, raise_exception, root, info, **args):
"""
Middleware resolver to check viewer's permissions
:param parent_resolver: Field resolver
:param permissions: Field permissions
:param attname: Field name
:param default_value: Default value to field if no resolver is provided
:param raise_exception: If True a PermissionDenied is raised
:param root: Schema root
:param info: Schema info
@ -127,6 +144,8 @@ def auth_resolver(parent_resolver, permissions, raise_exception, root, info, **a
if parent_resolver:
# A resolver is provided in the class
return resolve_bound_resolver(parent_resolver, root, info, **args)
# Get default resolver
return resolve_default_resolver(attname, default_value, root, info, **args)
elif raise_exception:
raise PermissionDenied()
return None