Browse Source

#921: load ACL providers after Django finishes loading models, via hook, instead of lazyload during request's lifecycle.

Rafał Pitoń 7 years ago
parent
commit
5ac1ed6496
4 changed files with 71 additions and 25 deletions
  1. 2 2
      misago/acl/api.py
  2. 4 1
      misago/acl/apps.py
  3. 21 7
      misago/acl/providers.py
  4. 44 15
      misago/acl/tests/test_providers.py

+ 2 - 2
misago/acl/api.py

@@ -51,7 +51,7 @@ def _add_acl_to_target(user, target):
     """add valid ACL to single target, helper for add_acl function"""
     target.acl = {}
 
-    for annotator in providers.get_type_annotators(target):
+    for annotator in providers.get_obj_type_annotators(target):
         annotator(user, target)
 
 
@@ -59,7 +59,7 @@ def serialize_acl(target):
     """serialize authenticated user's ACL"""
     serialized_acl = copy.deepcopy(target.acl_cache)
 
-    for serializer in providers.get_type_serializers(target):
+    for serializer in providers.get_obj_type_serializers(target):
         serializer(serialized_acl)
 
     return serialized_acl

+ 4 - 1
misago/acl/apps.py

@@ -1,7 +1,10 @@
 from django.apps import AppConfig
-
+from .providers import providers
 
 class MisagoACLsConfig(AppConfig):
     name = 'misago.acl'
     label = 'misago_acl'
     verbose_name = "Misago ACL framework"
+
+    def ready(self):
+        providers.load()

+ 21 - 7
misago/acl/providers.py

@@ -3,6 +3,18 @@ from importlib import import_module
 from misago.conf import settings
 
 
+_NOT_INITIALIZED_ERROR = (
+    "PermissionProviders instance has to load providers with load() "
+    "before get_obj_type_annotators(), get_obj_type_serializers(), "
+    "list() or dict() methods will be available."
+)
+
+_ALREADY_INITIALIZED_ERROR = (
+    "PermissionProviders instance has already loaded providers and "
+    "acl_annotator or acl_serializer are no longer available."
+)
+
+
 class PermissionProviders(object):
     """manager for permission providers"""
 
@@ -14,7 +26,7 @@ class PermissionProviders(object):
         self._annotators = {}
         self._serializers = {}
 
-    def _assert_providers_registered(self):
+    def load(self):
         if not self._initialized:
             self._register_providers()
             self._change_lists_to_tupes(self._annotators)
@@ -35,26 +47,28 @@ class PermissionProviders(object):
 
     def acl_annotator(self, hashable_type, func):
         """registers ACL annotator for specified types"""
+        assert not self._initialized, _ALREADY_INITIALIZED_ERROR
         self._annotators.setdefault(hashable_type, []).append(func)
 
     def acl_serializer(self, hashable_type, func):
         """registers ACL serializer for specified types"""
+        assert not self._initialized, _ALREADY_INITIALIZED_ERROR
         self._serializers.setdefault(hashable_type, []).append(func)
 
-    def get_type_annotators(self, obj):
-        self._assert_providers_registered()
+    def get_obj_type_annotators(self, obj):
+        assert self._initialized, _NOT_INITIALIZED_ERROR
         return self._annotators.get(obj.__class__, [])
 
-    def get_type_serializers(self, obj):
-        self._assert_providers_registered()
+    def get_obj_type_serializers(self, obj):
+        assert self._initialized, _NOT_INITIALIZED_ERROR
         return self._serializers.get(obj.__class__, [])
 
     def list(self):
-        self._assert_providers_registered()
+        assert self._initialized, _NOT_INITIALIZED_ERROR
         return self._providers
 
     def dict(self):
-        self._assert_providers_registered()
+        assert self._initialized, _NOT_INITIALIZED_ERROR
         return self._providers_dict
 
 

+ 44 - 15
misago/acl/tests/test_providers.py

@@ -20,19 +20,24 @@ class PermissionProvidersTests(TestCase):
         self.assertTrue(not providers._providers)
         self.assertTrue(not providers._providers_dict)
 
-        # list call initializes providers
-        providers_list = providers.list()
+        # public api errors on non-loaded object
+        with self.assertRaises(AssertionError):
+            providers.get_obj_type_annotators(TestType())
 
-        self.assertTrue(providers_list)
-        self.assertTrue(providers._initialized)
-        self.assertTrue(providers._providers)
-        self.assertTrue(providers._providers_dict)
+        with self.assertRaises(AssertionError):
+            providers.get_obj_type_serializers(TestType())
 
-        # dict call initializes providers
+        with self.assertRaises(AssertionError):
+            providers.list()
+
+        self.assertTrue(providers._initialized is False)
+        self.assertTrue(not providers._providers)
+        self.assertTrue(not providers._providers_dict)
+
+        # load initializes providers
         providers = PermissionProviders()
-        providers_dict = providers.dict()
+        providers.load()
 
-        self.assertTrue(providers_dict)
         self.assertTrue(providers._initialized)
         self.assertTrue(providers._providers)
         self.assertTrue(providers._providers_dict)
@@ -40,6 +45,13 @@ class PermissionProvidersTests(TestCase):
     def test_list(self):
         """providers manager list() returns iterable of tuples"""
         providers = PermissionProviders()
+
+        # providers.list() throws before loading providers
+        with self.assertRaises(AssertionError):
+            providers.list()
+
+        providers.load()
+
         providers_list = providers.list()
 
         providers_setting = settings.MISAGO_ACL_EXTENSIONS
@@ -52,6 +64,13 @@ class PermissionProvidersTests(TestCase):
     def test_dict(self):
         """providers manager dict() returns dict"""
         providers = PermissionProviders()
+
+        # providers.dict() throws before loading providers
+        with self.assertRaises(AssertionError):
+            providers.dict()
+
+        providers.load()
+
         providers_dict = providers.dict()
 
         providers_setting = settings.MISAGO_ACL_EXTENSIONS
@@ -63,22 +82,32 @@ class PermissionProvidersTests(TestCase):
 
     def test_annotators(self):
         """its possible to register and get annotators"""
-        providers = PermissionProviders()
-
         def mock_annotator(*args):
             pass
 
+        providers = PermissionProviders()
         providers.acl_annotator(TestType, mock_annotator)
-        annotators_list = providers.get_type_annotators(TestType())
+        providers.load()
+
+        # providers.acl_annotator() throws after loading providers
+        with self.assertRaises(AssertionError):
+            providers.acl_annotator(TestType, mock_annotator)
+
+        annotators_list = providers.get_obj_type_annotators(TestType())
         self.assertEqual(annotators_list[0], mock_annotator)
 
     def test_serializers(self):
         """its possible to register and get annotators"""
-        providers = PermissionProviders()
-
         def mock_serializer(*args):
             pass
 
+        providers = PermissionProviders()
         providers.acl_serializer(TestType, mock_serializer)
-        serializers_list = providers.get_type_serializers(TestType())
+        providers.load()
+
+        # providers.acl_serializer() throws after loading providers
+        with self.assertRaises(AssertionError):
+            providers.acl_serializer(TestType, mock_serializer)
+
+        serializers_list = providers.get_obj_type_serializers(TestType())
         self.assertEqual(serializers_list[0], mock_serializer)