mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-04-25 19:43:51 +03:00
Mixing DjangoObjectType and DjangoPermissionObjectType
This commit is contained in:
parent
120b94edd2
commit
7b86824afa
|
@ -165,4 +165,5 @@ class DjangoPermissionField(Field):
|
|||
|
||||
def get_resolver(self, parent_resolver):
|
||||
"""Intercept resolver to analyse permissions"""
|
||||
return partial(get_unbound_function(self.AUTH_RESOLVER), self.resolver or parent_resolver, self.permissions,None, None, True)
|
||||
return partial(get_unbound_function(self.AUTH_RESOLVER), self.resolver or parent_resolver, self.permissions,
|
||||
None, None, True)
|
||||
|
|
|
@ -4,7 +4,7 @@ from graphene import Interface, ObjectType, Schema, Connection, String, Field
|
|||
from graphene.relay import Node
|
||||
|
||||
from .. import registry
|
||||
from ..types import DjangoObjectType, DjangoObjectTypeOptions, DjangoPermissionObjectType
|
||||
from ..types import DjangoObjectType, DjangoObjectTypeOptions
|
||||
from .models import Article as ArticleModel
|
||||
from .models import Reporter as ReporterModel
|
||||
|
||||
|
@ -230,7 +230,7 @@ def extra_field_resolver(root, info, **kwargs):
|
|||
return 'extra field'
|
||||
|
||||
|
||||
class PermissionArticle(DjangoPermissionObjectType):
|
||||
class PermissionArticle(DjangoObjectType):
|
||||
"""Basic Type to test"""
|
||||
|
||||
class Meta(object):
|
||||
|
@ -257,7 +257,7 @@ def test_django_permissions():
|
|||
'reporter': ('content_type.permission3',),
|
||||
'extra_field': ('content_type.permission3',),
|
||||
}
|
||||
assert PermissionArticle._field_permissions == expected
|
||||
assert PermissionArticle._meta.field_permissions == expected
|
||||
|
||||
|
||||
def test_permission_resolver():
|
||||
|
|
|
@ -42,6 +42,22 @@ class DjangoObjectTypeOptions(ObjectTypeOptions):
|
|||
|
||||
|
||||
class DjangoObjectType(ObjectType):
|
||||
"""
|
||||
DjangoObjectType inheritance to handle field authorization
|
||||
Accepts field's permissions description as:
|
||||
|
||||
class Meta:
|
||||
|
||||
field_to_permission = {
|
||||
'restricted_field': ('permission1', 'permission2')
|
||||
}
|
||||
|
||||
permission_to_field = {
|
||||
'permission': ('restricted_field_1', 'restricted_field_2')
|
||||
}
|
||||
|
||||
At least one of the permissions must be accomplished in order to resolve the field.
|
||||
"""
|
||||
@classmethod
|
||||
def __init_subclass_with_meta__(
|
||||
cls,
|
||||
|
@ -55,6 +71,8 @@ class DjangoObjectType(ObjectType):
|
|||
connection_class=None,
|
||||
use_connection=None,
|
||||
interfaces=(),
|
||||
field_to_permission=None,
|
||||
permission_to_field=None,
|
||||
_meta=None,
|
||||
**options
|
||||
):
|
||||
|
@ -109,9 +127,67 @@ class DjangoObjectType(ObjectType):
|
|||
_meta=_meta, interfaces=interfaces, **options
|
||||
)
|
||||
|
||||
permissions = field_to_permission if field_to_permission else {}
|
||||
|
||||
if permission_to_field:
|
||||
permissions.update(cls.__get_permission_to_fields__(permission_to_field))
|
||||
|
||||
cls.field_permissions = permissions
|
||||
|
||||
for field_name, field_permissions in permissions.items():
|
||||
attr = 'resolve_{}'.format(field_name)
|
||||
resolver = getattr(_meta.fields[field_name], 'resolver', None) or getattr(cls, attr, None)
|
||||
|
||||
if not hasattr(field_permissions, '__iter__'):
|
||||
field_permissions = tuple(field_permissions)
|
||||
|
||||
setattr(cls, attr, cls.set_auth_resolver(field_name, field_permissions, resolver))
|
||||
|
||||
if permissions:
|
||||
cls.__set_as_nullable__(model, registry)
|
||||
|
||||
if not skip_registry:
|
||||
registry.register(cls)
|
||||
|
||||
@classmethod
|
||||
def __set_as_nullable__(cls, model, registry):
|
||||
"""Set restricted fields as nullable"""
|
||||
django_fields = yank_fields_from_attrs(
|
||||
construct_fields(model, registry, cls.field_permissions.keys(), ()),
|
||||
_as=Field,
|
||||
)
|
||||
for name, field in django_fields.items():
|
||||
if hasattr(field, '_type') and isinstance(field._type, NonNull):
|
||||
field._type = field._type._of_type
|
||||
setattr(cls, name, field)
|
||||
|
||||
@classmethod
|
||||
def __get_permission_to_fields__(cls, permission_to_field):
|
||||
"""
|
||||
Accepts a dictionary like
|
||||
{
|
||||
permission: [fields]
|
||||
}
|
||||
:return: Mapping of fields to permissions
|
||||
"""
|
||||
permissions = {}
|
||||
for permission, fields in permission_to_field.items():
|
||||
for field in fields:
|
||||
permissions[field] = permissions.get(field, ()) + (permission,)
|
||||
return permissions
|
||||
|
||||
@classmethod
|
||||
def set_auth_resolver(cls, name, permissions, resolver=None):
|
||||
"""
|
||||
Set middleware resolver to handle field permissions
|
||||
:param name: Field name
|
||||
:param permissions: List of permissions
|
||||
:param field: Meta's field
|
||||
:param resolver: Field resolver
|
||||
:return: Middleware resolver to check permissions
|
||||
"""
|
||||
return partial(auth_resolver, resolver, permissions, name, None, False)
|
||||
|
||||
def resolve_id(self, info):
|
||||
return self.pk
|
||||
|
||||
|
@ -134,90 +210,3 @@ class DjangoObjectType(ObjectType):
|
|||
return cls._meta.model.objects.get(pk=id)
|
||||
except cls._meta.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
|
||||
class DjangoPermissionObjectType(DjangoObjectType):
|
||||
"""
|
||||
DjangoObjectType inheritance to handle field authorization
|
||||
Accepts field's permissions description as:
|
||||
|
||||
class Meta:
|
||||
|
||||
field_to_permission = {
|
||||
'restricted_field': ('permission1', 'permission2')
|
||||
}
|
||||
|
||||
permission_to_field = {
|
||||
'permission': ('restricted_field_1', 'restricted_field_2')
|
||||
}
|
||||
|
||||
At least one of the permissions must be accomplished in order to resolve the field.
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
"""Meta Class"""
|
||||
abstract = True
|
||||
|
||||
@classmethod
|
||||
def __init_subclass_with_meta__(cls, field_to_permission=None, permission_to_field=None, model=None, registry=None,
|
||||
**options):
|
||||
super(DjangoPermissionObjectType, cls).__init_subclass_with_meta__(model=model, registry=registry, **options)
|
||||
|
||||
cls._field_permissions = field_to_permission if field_to_permission else {}
|
||||
|
||||
if permission_to_field:
|
||||
cls._get_permission_to_fields(permission_to_field)
|
||||
|
||||
for field_name, field_permissions in cls._field_permissions.items():
|
||||
attr = 'resolve_{}'.format(field_name)
|
||||
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)
|
||||
|
||||
setattr(cls, attr, cls.set_auth_resolver(field_name, field_permissions, resolver))
|
||||
|
||||
if cls._field_permissions:
|
||||
cls._set_as_nullable(model, registry)
|
||||
|
||||
@classmethod
|
||||
def _set_as_nullable(cls, model, registry):
|
||||
"""Set restricted fields as nullable"""
|
||||
django_fields = yank_fields_from_attrs(
|
||||
construct_fields(model, registry, cls._field_permissions.keys(), ()),
|
||||
_as=Field,
|
||||
)
|
||||
for name, field in django_fields.items():
|
||||
if hasattr(field, '_type') and isinstance(field._type, NonNull):
|
||||
field._type = field._type._of_type
|
||||
setattr(cls, name, field)
|
||||
|
||||
@classmethod
|
||||
def _get_permission_to_fields(cls, permission_to_field):
|
||||
"""
|
||||
Accepts a dictionary like
|
||||
{
|
||||
permission: [fields]
|
||||
}
|
||||
:return: Mapping of fields to permissions
|
||||
"""
|
||||
for permission, fields in permission_to_field.items():
|
||||
for field in fields:
|
||||
cls._set_permission_to_field(field, (permission,))
|
||||
|
||||
@classmethod
|
||||
def _set_permission_to_field(cls, field, permissions):
|
||||
"""Add list permissions to field"""
|
||||
cls._field_permissions[field] = cls._field_permissions.get(field, tuple()) + permissions
|
||||
|
||||
@classmethod
|
||||
def set_auth_resolver(cls, name, permissions, resolver=None):
|
||||
"""
|
||||
Set middleware resolver to handle field permissions
|
||||
:param name: Field name
|
||||
:param permissions: List of permissions
|
||||
:param field: Meta's field
|
||||
:param resolver: Field resolver
|
||||
:return: Middleware resolver to check permissions
|
||||
"""
|
||||
return partial(auth_resolver, resolver, permissions, name, None, False)
|
||||
|
|
Loading…
Reference in New Issue
Block a user