Merge pull request #3985 from ostcar/cleanup_access_permission

Refactor assess_permission
This commit is contained in:
Oskar Hahn 2018-12-16 15:12:35 +01:00 committed by GitHub
commit 5fc868cbee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 49 additions and 237 deletions

View File

@ -12,7 +12,9 @@ def check(args=None):
"""
Checks for pep8 and other code styling conventions.
"""
return call('flake8 --max-line-length=150 --statistics openslides tests')
value = call('flake8 --max-line-length=150 --statistics openslides tests')
value += call('python -m mypy openslides/ tests/')
return value
@command('travis', help='Runs the code that travis does')

View File

@ -10,17 +10,8 @@ class ItemAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'agenda.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import ItemSerializer
return ItemSerializer
# TODO: In the following method we use full_data['is_hidden'] and
# full_data['is_internal'] but this can be out of date.
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],

View File

@ -22,6 +22,7 @@ class AgendaAppConfig(AppConfig):
listen_to_related_object_post_delete,
listen_to_related_object_post_save)
from .views import ItemViewSet
from . import serializers # noqa
from ..utils.access_permissions import required_user
# Define projector elements.

View File

@ -1,7 +1,7 @@
from typing import Any, Dict, List
from ..utils.access_permissions import BaseAccessPermissions
from ..utils.auth import async_has_perm, has_perm
from ..utils.auth import async_has_perm
class AssignmentAccessPermissions(BaseAccessPermissions):
@ -10,18 +10,6 @@ class AssignmentAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'assignments.can_see'
def get_serializer_class(self, user=None):
"""
Returns different serializer classes according to users permissions.
"""
from .serializers import AssignmentFullSerializer, AssignmentShortSerializer
if user is None or (has_perm(user, 'assignments.can_see') and has_perm(user, 'assignments.can_manage')):
serializer_class = AssignmentFullSerializer
else:
serializer_class = AssignmentShortSerializer
return serializer_class
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],

View File

@ -15,11 +15,12 @@ class AssignmentsAppConfig(AppConfig):
def ready(self):
# Import all required stuff.
from ..core.signals import permission_change
from ..utils.access_permissions import required_user
from ..utils.rest_api import router
from . import serializers # noqa
from .projector import get_projector_elements
from .signals import get_permission_change_data
from .views import AssignmentViewSet, AssignmentPollViewSet
from ..utils.access_permissions import required_user
# Define projector elements.
register_projector_elements(get_projector_elements())

View File

@ -238,31 +238,3 @@ class AssignmentFullSerializer(ModelSerializer):
assignment.agenda_item_update_information['parent_id'] = agenda_parent_id
assignment.save()
return assignment
class AssignmentShortSerializer(AssignmentFullSerializer):
"""
Serializer for assignment.models.Assignment objects. Without unpublished poll.
"""
assignment_related_users = AssignmentRelatedUserSerializer(many=True, read_only=True)
polls = AssignmentShortPollSerializer(many=True, read_only=True)
class Meta:
model = Assignment
fields = (
'id',
'title',
'description',
'open_posts',
'phase',
'assignment_related_users',
'poll_description_default',
'polls',
'agenda_item_id',
'tags',)
validators = (posts_validator,)
def validate(self, data):
if 'description' in data:
data['description'] = validate_html(data['description'])
return data

View File

@ -8,28 +8,12 @@ class ProjectorAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'core.can_see_projector'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import ProjectorSerializer
return ProjectorSerializer
class TagAccessPermissions(BaseAccessPermissions):
"""
Access permissions container for Tag and TagViewSet.
"""
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import TagSerializer
return TagSerializer
class ChatMessageAccessPermissions(BaseAccessPermissions):
"""
@ -37,14 +21,6 @@ class ChatMessageAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'core.can_use_chat'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import ChatMessageSerializer
return ChatMessageSerializer
class ProjectorMessageAccessPermissions(BaseAccessPermissions):
"""
@ -52,14 +28,6 @@ class ProjectorMessageAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'core.can_see_projector'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import ProjectorMessageSerializer
return ProjectorMessageSerializer
class CountdownAccessPermissions(BaseAccessPermissions):
"""
@ -67,14 +35,6 @@ class CountdownAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'core.can_see_projector'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import CountdownSerializer
return CountdownSerializer
class ConfigAccessPermissions(BaseAccessPermissions):
"""
@ -82,14 +42,6 @@ class ConfigAccessPermissions(BaseAccessPermissions):
ConfigViewSet).
"""
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import ConfigSerializer
return ConfigSerializer
class HistoryAccessPermissions(BaseAccessPermissions):
"""
@ -102,11 +54,3 @@ class HistoryAccessPermissions(BaseAccessPermissions):
model instances.
"""
return await async_in_some_groups(user_id, [GROUP_ADMIN_PK])
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import HistorySerializer
return HistorySerializer

View File

@ -22,6 +22,7 @@ class CoreAppConfig(AppConfig):
from ..utils.rest_api import router
from ..utils.cache import element_cache
from .projector import get_projector_elements
from . import serializers # noqa
from .signals import (
delete_django_app_permissions,
get_permission_change_data,

View File

@ -10,14 +10,6 @@ class MediafileAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'mediafiles.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import MediafileSerializer
return MediafileSerializer
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],

View File

@ -18,6 +18,7 @@ class MediafilesAppConfig(AppConfig):
from .projector import get_projector_elements
from .signals import get_permission_change_data
from .views import MediafileViewSet
from . import serializers # noqa
from ..utils.access_permissions import required_user
# Define projector elements.

View File

@ -11,14 +11,6 @@ class MotionAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'motions.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import MotionSerializer
return MotionSerializer
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],
@ -71,14 +63,6 @@ class MotionChangeRecommendationAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'motions.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import MotionChangeRecommendationSerializer
return MotionChangeRecommendationSerializer
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],
@ -107,14 +91,6 @@ class MotionCommentSectionAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'motions.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import MotionCommentSectionSerializer
return MotionCommentSectionSerializer
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],
@ -140,14 +116,6 @@ class StatuteParagraphAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'motions.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import StatuteParagraphSerializer
return StatuteParagraphSerializer
class CategoryAccessPermissions(BaseAccessPermissions):
"""
@ -155,14 +123,6 @@ class CategoryAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'motions.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import CategorySerializer
return CategorySerializer
class MotionBlockAccessPermissions(BaseAccessPermissions):
"""
@ -170,25 +130,9 @@ class MotionBlockAccessPermissions(BaseAccessPermissions):
"""
base_permission = 'motions.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import MotionBlockSerializer
return MotionBlockSerializer
class WorkflowAccessPermissions(BaseAccessPermissions):
"""
Access permissions container for Workflow and WorkflowViewSet.
"""
base_permission = 'motions.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import WorkflowSerializer
return WorkflowSerializer

View File

@ -21,6 +21,7 @@ class MotionsAppConfig(AppConfig):
create_builtin_workflows,
get_permission_change_data,
)
from . import serializers # noqa
from .views import (
CategoryViewSet,
StatuteParagraphViewSet,

View File

@ -6,11 +6,3 @@ class TopicAccessPermissions(BaseAccessPermissions):
Access permissions container for Topic and TopicViewSet.
"""
base_permission = 'agenda.can_see'
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import TopicSerializer
return TopicSerializer

View File

@ -16,6 +16,7 @@ class TopicsAppConfig(AppConfig):
from .projector import get_projector_elements
from .signals import get_permission_change_data
from .views import TopicViewSet
from . import serializers # noqa
# Define projector elements.
register_projector_elements(get_projector_elements())

View File

@ -10,14 +10,6 @@ class UserAccessPermissions(BaseAccessPermissions):
Access permissions container for User and UserViewSet.
"""
def get_serializer_class(self, user=None):
"""
Returns different serializer classes with respect user's permissions.
"""
from .serializers import UserFullSerializer
return UserFullSerializer
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],
@ -95,14 +87,6 @@ class GroupAccessPermissions(BaseAccessPermissions):
Access permissions container for Groups. Everyone can see them
"""
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import GroupSerializer
return GroupSerializer
class PersonalNoteAccessPermissions(BaseAccessPermissions):
"""
@ -110,14 +94,6 @@ class PersonalNoteAccessPermissions(BaseAccessPermissions):
can handle personal notes.
"""
def get_serializer_class(self, user=None):
"""
Returns serializer class.
"""
from .serializers import PersonalNoteSerializer
return PersonalNoteSerializer
async def get_restricted_data(
self,
full_data: List[Dict[str, Any]],

View File

@ -1,8 +1,6 @@
from typing import Any, Callable, Dict, List, Set
from asgiref.sync import async_to_sync
from django.db.models import Model
from rest_framework.serializers import Serializer
from .auth import async_anonymous_is_enabled, async_has_perm, user_to_user_id
from .cache import element_cache
@ -41,25 +39,6 @@ class BaseAccessPermissions:
else:
return bool(user_id) or await async_anonymous_is_enabled()
def get_serializer_class(self, user_id: int = 0) -> Serializer:
"""
Returns different serializer classes according to users permissions.
This should return the serializer for full data access if user is
None. See get_full_data().
"""
# TODO: Rewrite me by using an serializer_class attribute and removing
# the user_id argument.
raise NotImplementedError(
"You have to add the method 'get_serializer_class' to your "
"access permissions class.".format(self))
def get_full_data(self, instance: Model) -> Dict[str, Any]:
"""
Returns all possible serialized data for the given instance.
"""
return self.get_serializer_class()(instance).data
async def get_restricted_data(
self, full_data: List[Dict[str, Any]],
user_id: int) -> List[Dict[str, Any]]:
@ -71,11 +50,6 @@ class BaseAccessPermissions:
the return is the same. Returns an empty list if the user has no read
access. Returns reduced data if the user has limited access. Default:
Returns full data if the user has read access to model instances.
Hint: You should override this method if your get_serializer_class()
method returns different serializers for different users or if you
have access restrictions in your view or viewset in methods like
retrieve() or list().
"""
return full_data if await self.async_check_permissions(user_id) else []

View File

@ -4,6 +4,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.db import models
from .access_permissions import BaseAccessPermissions
from .rest_api import model_serializer_classes
from .utils import convert_camel_case_to_pseudo_snake_case
@ -134,4 +135,5 @@ class RESTModelMixin:
"""
Returns the full_data of the instance.
"""
return self.get_access_permissions().get_full_data(self)
serializer_class = model_serializer_classes[type(self)]
return serializer_class(self).data

View File

@ -2,6 +2,7 @@ from collections import OrderedDict
from typing import Any, Dict, Iterable, Optional, Type
from asgiref.sync import async_to_sync
from django.db.models import Model
from django.http import Http404
from rest_framework import status
from rest_framework.decorators import detail_route, list_route
@ -32,6 +33,7 @@ from rest_framework.serializers import (
PrimaryKeyRelatedField,
RelatedField,
Serializer,
SerializerMetaclass,
SerializerMethodField,
ValidationError,
)
@ -152,17 +154,44 @@ class PermissionMixin:
def get_serializer_class(self) -> Type[Serializer]:
"""
Overridden method to return the serializer class given by the
access permissions container.
Overridden method to return the serializer class for the model.
"""
if self.get_access_permissions() is not None:
serializer_class = self.get_access_permissions().get_serializer_class(self.request.user) # type: ignore
model = self.get_queryset().model # type: ignore
try:
return model_serializer_classes[model]
except AttributeError:
# If there is no known serializer class for the model, return the
# default serializer class.
return super().get_serializer_class() # type: ignore
model_serializer_classes: Dict[Type[Model], Serializer] = {}
class ModelSerializerRegisterer(SerializerMetaclass):
"""
Meta class for model serializer that detects the corresponding model
and saves it.
"""
def __new__(cls, name, bases, attrs): # type: ignore
"""
Detects the corresponding model from the ModelSerializer by
looking into the Meta-class.
Does nothing, if the Meta-class does not have the model attribute.
"""
serializer_class = super().__new__(cls, name, bases, attrs)
try:
model = serializer_class.Meta.model
except AttributeError:
pass
else:
serializer_class = super().get_serializer_class() # type: ignore
model_serializer_classes[model] = serializer_class
return serializer_class
class ModelSerializer(_ModelSerializer):
class ModelSerializer(_ModelSerializer, metaclass=ModelSerializerRegisterer):
"""
ModelSerializer that changes the field names of related fields to
FIELD_NAME_id.