Browse Source

Add non-global acl implementation and testing utils

rafalp 6 years ago
parent
commit
58cbc437ef

+ 1 - 0
devproject/settings.py

@@ -226,6 +226,7 @@ MIDDLEWARE = [
 
     'misago.cache.middleware.cache_versions_middleware',
     'misago.users.middleware.UserMiddleware',
+    'misago.acl.middleware.user_acl_middleware',
     'misago.core.middleware.ExceptionHandlerMiddleware',
     'misago.users.middleware.OnlineTrackerMiddleware',
     'misago.admin.middleware.AdminAuthMiddleware',

+ 1 - 1
misago/acl/api.py

@@ -14,7 +14,7 @@ from misago.core import threadstore
 from misago.core.cache import cache
 
 from . import version
-from .builder import build_acl
+from .buildacl import build_acl
 from .providers import providers
 
 

+ 0 - 0
misago/acl/builder.py → misago/acl/buildacl.py


+ 12 - 0
misago/acl/middleware.py

@@ -0,0 +1,12 @@
+from django.utils.functional import SimpleLazyObject
+
+from . import useracl
+
+
+def user_acl_middleware(get_response):
+    """Sets request.cache_versions attribute with dict of cache versions."""
+    def middleware(request):
+        request.user_acl = useracl.get_user_acl(request.user, request.cache_versions)
+        return get_response(request)
+
+    return middleware

+ 35 - 0
misago/acl/test.py

@@ -0,0 +1,35 @@
+from functools import wraps
+from unittest.mock import patch
+
+from .useracl import get_user_acl
+
+
+class PatchUserACL:
+    def patch_user_acl(self, user, patch):
+        self.patches[user.id] = patch
+
+    def patched_get_user_acl(self, user, cache_versions):
+        user_acl = get_user_acl(user, cache_versions)
+        user_acl.update(self.patches.get(user.id, {}))
+        return user_acl
+
+    def __enter__(self):
+        self.patches = {}
+
+    def __exit__(self, *_):
+        self.patches = {}
+
+    def __call__(self, f):
+        @wraps(f)
+        def inner(*args, **kwargs):
+            with self as context:
+                with patch(
+                    "misago.acl.useracl.get_user_acl",
+                    side_effect=self.patched_get_user_acl,
+                ):
+                    return f(*args, self.patch_user_acl, **kwargs)
+        
+        return inner
+
+
+patch_user_acl = PatchUserACL()

+ 70 - 0
misago/acl/tests/test_getting_user_acl.py

@@ -0,0 +1,70 @@
+from unittest.mock import patch
+
+from django.contrib.auth import get_user_model
+from django.test import TestCase
+
+from misago.acl.useracl import get_user_acl
+from misago.users.models import AnonymousUser
+
+User = get_user_model()
+
+cache_versions = {"acl": "abcdefgh"}
+
+
+class GettingUserACLTests(TestCase):
+    def test_getter_returns_authenticated_user_acl(self):
+        user = User.objects.create_user('Bob', 'bob@bob.com')
+        acl = get_user_acl(user, cache_versions)
+
+        assert acl
+        assert acl["user_id"] == user.id
+        assert acl["is_authenticated"] is True
+        assert acl["is_anonymous"] is False
+
+    def test_getter_returns_anonymous_user_acl(self):
+        user = AnonymousUser()
+        acl = get_user_acl(user, cache_versions)
+
+        assert acl
+        assert acl["user_id"] == user.id
+        assert acl["is_authenticated"] is False
+        assert acl["is_anonymous"] is True
+
+    @patch('django.core.cache.cache.get', return_value=dict())
+    def test_getter_returns_acl_from_cache(self, cache_get):
+        user = AnonymousUser()
+        get_user_acl(user, cache_versions)
+        cache_get.assert_called_once()
+
+    @patch('django.core.cache.cache.set')
+    @patch('misago.acl.buildacl.build_acl', return_value=dict())
+    @patch('django.core.cache.cache.get', return_value=None)
+    def test_getter_builds_new_acl_when_cache_is_not_available(self, cache_get, *_):
+        user = AnonymousUser()
+        get_user_acl(user, cache_versions)
+        cache_get.assert_called_once()
+
+    @patch('django.core.cache.cache.set')
+    @patch('misago.acl.buildacl.build_acl', return_value=dict())
+    @patch('django.core.cache.cache.get', return_value=None)
+    def test_getter_sets_new_cache_if_no_cache_is_set(self, cache_set, *_):
+        user = AnonymousUser()
+        get_user_acl(user, cache_versions)
+        cache_set.assert_called_once()
+
+
+    @patch('django.core.cache.cache.set')
+    @patch('misago.acl.buildacl.build_acl', return_value=dict())
+    @patch('django.core.cache.cache.get', return_value=None)
+    def test_acl_cache_name_includes_cache_verssion(self, cache_set, *_):
+        user = AnonymousUser()
+        get_user_acl(user, cache_versions)
+        cache_key = cache_set.call_args[0][0]
+        assert cache_versions["acl"] in cache_key
+
+    @patch('django.core.cache.cache.set')
+    @patch('django.core.cache.cache.get', return_value=dict())
+    def test_getter_is_not_setting_new_cache_if_cache_is_set(self, _, cache_set):
+        user = AnonymousUser()
+        get_user_acl(user, cache_versions)
+        cache_set.assert_not_called()

+ 42 - 0
misago/acl/tests/test_patching_user_acl_in_tests.py

@@ -0,0 +1,42 @@
+from django.contrib.auth import get_user_model
+from django.test import TestCase
+
+from misago.acl import useracl
+from misago.acl.test import patch_user_acl
+
+User = get_user_model()
+
+cache_versions = {"acl": "abcdefgh"}
+
+
+class PatchingUserACLInTestsTests(TestCase):
+    @patch_user_acl
+    def test_decorator_adds_patching_function_to_test(self, patch_user_acl):
+        assert patch_user_acl
+
+    @patch_user_acl
+    def test_patching_function_changes_user_permission_value(self, patch_user_acl):
+        user = User.objects.create_user("User", "user@example.com")
+        patch_user_acl(user, {"can_rename_users": 123})
+        user_acl = useracl.get_user_acl(user, cache_versions)
+        assert user_acl["can_rename_users"] == 123
+
+    @patch_user_acl
+    def test_patching_function_adds_user_permission(self, patch_user_acl):
+        user = User.objects.create_user("User", "user@example.com")
+        patch_user_acl(user, {"new_user_permission": 123})
+        user_acl = useracl.get_user_acl(user, cache_versions)
+        assert user_acl["new_user_permission"] == 123
+
+    def test_acl_patches_are_removed_after_test(self):
+        user = User.objects.create_user("User", "user@example.com")
+
+        @patch_user_acl
+        def test_function(patch_user_acl):
+            patch_user_acl(user, {"is_patched": True})
+            user_acl = useracl.get_user_acl(user, cache_versions)
+            assert user_acl["is_patched"]
+
+        user_acl = useracl.get_user_acl(user, cache_versions)
+        assert "is_patched" not in user_acl
+    

+ 29 - 0
misago/acl/tests/test_user_acl_middleware.py

@@ -0,0 +1,29 @@
+from unittest.mock import Mock
+
+from django.contrib.auth import get_user_model
+from django.test import TestCase
+
+from misago.acl.middleware import user_acl_middleware
+
+User = get_user_model()
+
+cache_versions = {"acl": "abcdefgh"}
+
+
+class MiddlewareTests(TestCase):
+    def test_middleware_sets_attr_on_request(self):
+        user = User.objects.create_user("User", "user@example.com")
+
+        get_response = Mock()
+        request = Mock(user=user, cache_versions=cache_versions)
+        middleware = user_acl_middleware(get_response)
+        middleware(request)
+        assert request.user_acl
+
+    def test_middleware_calls_get_response(self):
+        user = User.objects.create_user("User", "user@example.com")
+        get_response = Mock()
+        request = Mock(user=user, cache_versions=cache_versions)
+        middleware = user_acl_middleware(get_response)
+        middleware(request)
+        get_response.assert_called_once()

+ 15 - 0
misago/acl/useracl.py

@@ -0,0 +1,15 @@
+from django.core.cache import cache
+
+from . import buildacl
+
+
+def get_user_acl(user, cache_versions):
+    cache_name = 'acl_%s_%s' % (user.acl_key, cache_versions["acl"])
+    user_acl = cache.get(cache_name)
+    if user_acl is None:
+        user_acl = buildacl.build_acl(user.get_roles())
+        cache.set(cache_name, user_acl)
+    user_acl["user_id"] = user.id
+    user_acl["is_authenticated"] = bool(user.is_authenticated)
+    user_acl["is_anonymous"] = bool(user.is_anonymous)
+    return user_acl

+ 1 - 3
misago/cache/middleware.py

@@ -1,12 +1,10 @@
-from django.utils.functional import SimpleLazyObject
-
 from .versions import get_cache_versions
 
 
 def cache_versions_middleware(get_response):
     """Sets request.cache_versions attribute with dict of cache versions."""
     def middleware(request):
-        request.cache_versions = SimpleLazyObject(get_cache_versions)
+        request.cache_versions = get_cache_versions()
         return get_response(request)
 
     return middleware

+ 2 - 30
misago/cache/tests/test_cache_versions_middleware.py

@@ -1,7 +1,6 @@
-from unittest.mock import Mock, PropertyMock, patch
+from unittest.mock import Mock
 
 from django.test import TestCase
-from django.utils.functional import SimpleLazyObject
 
 from misago.cache.versions import CACHE_NAME
 from misago.cache.middleware import cache_versions_middleware
@@ -11,21 +10,9 @@ class MiddlewareTests(TestCase):
     def test_middleware_sets_attr_on_request(self):
         get_response = Mock()
         request = Mock()
-        cache_versions = PropertyMock()
-        type(request).cache_versions = cache_versions
         middleware = cache_versions_middleware(get_response)
         middleware(request)
-        cache_versions.assert_called_once()
-
-    def test_attr_set_by_middleware_on_request_is_lazy_object(self):
-        get_response = Mock()
-        request = Mock()
-        cache_versions = PropertyMock()
-        type(request).cache_versions = cache_versions
-        middleware = cache_versions_middleware(get_response)
-        middleware(request)
-        attr_value = cache_versions.call_args[0][0]
-        assert isinstance(attr_value, SimpleLazyObject)
+        assert request.cache_versions
 
     def test_middleware_calls_get_response(self):
         get_response = Mock()
@@ -33,18 +20,3 @@ class MiddlewareTests(TestCase):
         middleware = cache_versions_middleware(get_response)
         middleware(request)
         get_response.assert_called_once()
-
-    def test_middleware_is_not_reading_db(self):
-        get_response = Mock()
-        request = Mock()
-        with self.assertNumQueries(0):
-            middleware = cache_versions_middleware(get_response)
-            middleware(request)
-
-    @patch('django.core.cache.cache.get')
-    def test_middleware_is_not_reading_cache(self, cache_get):
-        get_response = Mock()
-        request = Mock()
-        middleware = cache_versions_middleware(get_response)
-        middleware(request)
-        cache_get.assert_not_called()

+ 3 - 3
misago/cache/tests/test_getting_cache_versions.py

@@ -32,13 +32,13 @@ class CacheVersionsTests(TestCase):
     @patch('django.core.cache.cache.set')
     @patch('django.core.cache.cache.get', return_value=None)
     def test_getter_sets_new_cache_if_no_cache_is_set(self, _, cache_set):
-        assert get_cache_versions()
+        get_cache_versions()
         db_caches = get_cache_versions_from_db()
         cache_set.assert_called_once_with(CACHE_NAME, db_caches)
 
     @patch('django.core.cache.cache.set')
     @patch('django.core.cache.cache.get', return_value=True)
     def test_getter_is_not_setting_new_cache_if_cache_is_set(self, _, cache_set):
-        assert get_cache_versions()
-        db_caches = get_cache_versions_from_db()
+        get_cache_versions()
+        get_cache_versions_from_db()
         cache_set.assert_not_called()