From b1c28201283734a5a7969a95beedff40efeba51b Mon Sep 17 00:00:00 2001 From: Behoston Date: Fri, 15 Mar 2019 10:27:08 +0100 Subject: [PATCH] Add protected views. --- flask_jsonapi/permissions/__init__.py | 6 ++ flask_jsonapi/permissions/views.py | 79 +++++++++++++++++++ tests/conftest.py | 13 +++ tests/permissions/__init__.py | 0 .../test_checkers.py} | 0 5 files changed, 98 insertions(+) create mode 100644 flask_jsonapi/permissions/views.py create mode 100644 tests/permissions/__init__.py rename tests/{test_permissions.py => permissions/test_checkers.py} (100%) diff --git a/flask_jsonapi/permissions/__init__.py b/flask_jsonapi/permissions/__init__.py index 77269fe..d4277c4 100644 --- a/flask_jsonapi/permissions/__init__.py +++ b/flask_jsonapi/permissions/__init__.py @@ -6,6 +6,9 @@ from .checkers import ObjectLevelPermissionChecker from .checkers import PermissionChecker from .checkers import PermissionException +from .views import ProtectedDetailView +from .views import ProtectedListView +from .views import ProtectedViewSet __all__ = [ CREATE_ACTION, @@ -16,4 +19,7 @@ PermissionChecker, ObjectLevelPermissionChecker, PermissionException, + ProtectedDetailView, + ProtectedListView, + ProtectedViewSet, ] diff --git a/flask_jsonapi/permissions/views.py b/flask_jsonapi/permissions/views.py new file mode 100644 index 0000000..0ebc159 --- /dev/null +++ b/flask_jsonapi/permissions/views.py @@ -0,0 +1,79 @@ +import abc +import logging + +from flask_jsonapi import ResourceRepositoryDetailView +from flask_jsonapi import ResourceRepositoryListView +from flask_jsonapi import ResourceRepositoryViewSet +from flask_jsonapi import descriptors +from flask_jsonapi import exceptions + +from . import checkers + +logger = logging.getLogger(__name__) + + +class ProtectedDetailView(ResourceRepositoryDetailView): + def __init__(self, permission_checker: checkers.PermissionChecker, **kwargs): + super().__init__(**kwargs) + self.permission_checker = permission_checker + + def read(self, id): + resource = super().read(id) + resource = self.permission_checker.check_read_permission(resource=resource) + return resource + + def destroy(self, id): + resource = super().read(id) + resource = self.permission_checker.check_destroy_permission(resource=resource) + return super().destroy(resource.id) + + def update(self, id, data, **kwargs): + resource = super().read(id) + resource, data = self.permission_checker.check_update_permission(resource=resource, data=data) + return super().update(id, data, **kwargs) + + +class ProtectedListView(ResourceRepositoryListView, abc.ABC): + def __init__(self, permission_checker: checkers.PermissionChecker, **kwargs): + super().__init__(**kwargs) + self.permission_checker = permission_checker + + def get(self, *args, **kwargs): + try: + return super().get(*args, **kwargs) + except checkers.PermissionException: + raise exceptions.ForbiddenError("You don't have permissions") + + def post(self, *args, **kwargs): + try: + return super().post(*args, **kwargs) + except checkers.PermissionException: + raise exceptions.ForbiddenError("You don't have permissions") + + def read_many(self, filters: dict, **kwargs): + filters = self._apply_permission_filter(filters) + resources = super().read_many(filters, **kwargs) + length_before_permission = len(resources) + resources = self.permission_checker.check_list_permission(resources=resources) + if length_before_permission != len(resources): + logger.warning('No permission for some items!', extra=kwargs) + return resources + + @abc.abstractmethod + def _apply_permission_filter(self, filters: dict) -> dict: + pass + + def create(self, data: dict, **kwargs): + data = self.permission_checker.check_create_permission(data=data) + return super().create(data, **kwargs) + + +class ProtectedViewSet(ResourceRepositoryViewSet): + detail_view_cls = ProtectedDetailView + list_view_cls: ProtectedListView + permission_checker: checkers.PermissionChecker = descriptors.NotImplementedProperty('permission_checker') + + def get_views_kwargs(self): + kwargs = super().get_views_kwargs() + kwargs['permission_checker'] = self.permission_checker + return kwargs diff --git a/tests/conftest.py b/tests/conftest.py index 1d176f6..2bf7bbf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,3 +6,16 @@ def app(): application = flask.Flask(__name__) return application + + +@pytest.fixture +def api(app): + from flask_jsonapi import api + application_api = api.Api(app) + return application_api + + +@pytest.fixture +def test_client(app): + with app.test_client() as client: + yield client diff --git a/tests/permissions/__init__.py b/tests/permissions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_permissions.py b/tests/permissions/test_checkers.py similarity index 100% rename from tests/test_permissions.py rename to tests/permissions/test_checkers.py