wbcore 1.61.2__py2.py3-none-any.whl → 1.61.4__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wbcore/configurations/base.py +2 -2
- wbcore/configurations/configurations/apps.py +1 -1
- wbcore/configurations/configurations/rest_framework.py +1 -1
- wbcore/contrib/agenda/viewsets/menu/calendar_items.py +1 -2
- wbcore/contrib/agenda/viewsets/menu/conference_room.py +4 -5
- wbcore/contrib/authentication/admin.py +15 -2
- wbcore/contrib/authentication/factories/users.py +4 -2
- wbcore/contrib/authentication/migrations/0008_user_is_internal.py +18 -0
- wbcore/contrib/authentication/models/users.py +16 -6
- wbcore/contrib/authentication/serializers/users.py +1 -1
- wbcore/contrib/authentication/viewsets/menu/user_activities.py +6 -3
- wbcore/contrib/authentication/viewsets/menu/users.py +1 -2
- wbcore/contrib/authentication/viewsets/users.py +2 -2
- wbcore/contrib/currency/tests/test_serializers.py +3 -1
- wbcore/contrib/directory/admin/entries.py +1 -1
- wbcore/contrib/directory/models/entries.py +21 -30
- wbcore/contrib/directory/tests/test_permissions.py +2 -4
- wbcore/contrib/directory/viewsets/endpoints/relationships.py +1 -2
- wbcore/contrib/directory/viewsets/entries.py +1 -1
- wbcore/contrib/directory/viewsets/menu/contacts.py +2 -3
- wbcore/contrib/directory/viewsets/menu/entries.py +5 -14
- wbcore/contrib/directory/viewsets/menu/relationships.py +2 -3
- wbcore/contrib/directory/viewsets/menu/utils.py +8 -9
- wbcore/contrib/documents/models/documents.py +1 -1
- wbcore/contrib/documents/viewsets/documents.py +3 -3
- wbcore/contrib/geography/viewsets/geography.py +1 -1
- wbcore/contrib/permission/apps.py +15 -0
- wbcore/contrib/permission/configurations.py +3 -0
- wbcore/contrib/{guardian → permission}/filters.py +2 -2
- wbcore/contrib/permission/internal/registry.py +43 -0
- wbcore/contrib/permission/management/__init__.py +4 -0
- wbcore/contrib/{guardian → permission}/models/mixins.py +73 -3
- wbcore/{permissions → contrib/permission}/permissions.py +1 -3
- wbcore/contrib/{guardian → permission}/tasks.py +1 -1
- wbcore/{tests/test_permissions → contrib/permission/tests}/test_backend.py +4 -6
- wbcore/contrib/{guardian → permission}/tests/test_model_mixins.py +6 -6
- wbcore/contrib/{guardian → permission}/tests/test_tasks.py +7 -7
- wbcore/contrib/{guardian → permission}/tests/test_utils.py +12 -12
- wbcore/contrib/{guardian → permission}/tests/test_viewsets.py +3 -3
- wbcore/contrib/{guardian → permission}/urls.py +1 -1
- wbcore/contrib/{guardian → permission}/utils.py +32 -4
- wbcore/contrib/{guardian → permission}/viewsets/configs/buttons.py +2 -2
- wbcore/contrib/{guardian → permission}/viewsets/configs/displays.py +1 -1
- wbcore/contrib/{guardian → permission}/viewsets/configs/endpoints.py +3 -3
- wbcore/contrib/{guardian → permission}/viewsets/configs/titles.py +1 -1
- wbcore/contrib/{guardian → permission}/viewsets/mixins.py +2 -2
- wbcore/contrib/{guardian → permission}/viewsets/viewsets.py +2 -2
- wbcore/contrib/workflow/viewsets/menu/condition.py +2 -3
- wbcore/contrib/workflow/viewsets/menu/data.py +2 -7
- wbcore/contrib/workflow/viewsets/menu/process.py +2 -5
- wbcore/contrib/workflow/viewsets/menu/step.py +16 -21
- wbcore/contrib/workflow/viewsets/menu/transition.py +2 -3
- wbcore/contrib/workflow/viewsets/menu/workflow.py +2 -5
- wbcore/tests/models.py +1 -1
- wbcore/urls.py +2 -2
- {wbcore-1.61.2.dist-info → wbcore-1.61.4.dist-info}/METADATA +1 -1
- {wbcore-1.61.2.dist-info → wbcore-1.61.4.dist-info}/RECORD +69 -70
- wbcore/contrib/guardian/apps.py +0 -6
- wbcore/contrib/guardian/configurations.py +0 -3
- wbcore/permissions/mixins.py +0 -72
- wbcore/permissions/registry.py +0 -33
- wbcore/permissions/shortcuts.py +0 -38
- wbcore/permissions/utils.py +0 -26
- /wbcore/contrib/{guardian → permission}/__init__.py +0 -0
- /wbcore/contrib/{guardian/migrations → permission/internal}/__init__.py +0 -0
- /wbcore/{permissions → contrib/permission/internal}/backend.py +0 -0
- /wbcore/contrib/{guardian → permission}/migrations/0001_initial.py +0 -0
- /wbcore/contrib/{guardian/tests → permission/migrations}/__init__.py +0 -0
- /wbcore/contrib/{guardian → permission}/models/__init__.py +0 -0
- /wbcore/contrib/{guardian → permission}/models/models.py +0 -0
- /wbcore/{permissions → contrib/permission/tests}/__init__.py +0 -0
- /wbcore/contrib/{guardian → permission}/tests/conftest.py +0 -0
- /wbcore/contrib/{guardian → permission}/viewsets/__init__.py +0 -0
- /wbcore/contrib/{guardian → permission}/viewsets/configs/__init__.py +0 -0
- {wbcore-1.61.2.dist-info → wbcore-1.61.4.dist-info}/WHEEL +0 -0
|
@@ -24,10 +24,10 @@ from wbcore.contrib.documents.viewsets.display import DocumentModelDisplay
|
|
|
24
24
|
from wbcore.contrib.documents.viewsets.endpoints import DocumentEndpointConfig
|
|
25
25
|
from wbcore.contrib.documents.viewsets.previews import DocumentPreviewConfig
|
|
26
26
|
from wbcore.contrib.documents.viewsets.titles import DocumentModelTitleConfig
|
|
27
|
-
from wbcore.contrib.
|
|
27
|
+
from wbcore.contrib.permission.viewsets.mixins import ObjectPermissionFilterMixin
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
class DocumentModelViewSet(
|
|
30
|
+
class DocumentModelViewSet(ObjectPermissionFilterMixin, viewsets.ModelViewSet):
|
|
31
31
|
LIST_DOCUMENTATION = "documents/markdown/documentation/documents.md"
|
|
32
32
|
search_fields = ("name",)
|
|
33
33
|
ordering = ["-updated", "name"]
|
|
@@ -101,7 +101,7 @@ class DocumentModelViewSet(GuardianFilterMixin, viewsets.ModelViewSet):
|
|
|
101
101
|
return HttpResponse("Document could not be found", status=status.HTTP_404_NOT_FOUND)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
class DocumentRepresentationViewSet(
|
|
104
|
+
class DocumentRepresentationViewSet(ObjectPermissionFilterMixin, viewsets.RepresentationViewSet):
|
|
105
105
|
IDENTIFIER = "wbcore:documents:documentrepresentation"
|
|
106
106
|
serializer_class = DocumentRepresentationSerializer
|
|
107
107
|
queryset = Document.objects.all()
|
|
@@ -3,7 +3,7 @@ from django.db.models.expressions import Value
|
|
|
3
3
|
from rest_framework.filters import OrderingFilter
|
|
4
4
|
|
|
5
5
|
from wbcore import viewsets
|
|
6
|
-
from wbcore.contrib.
|
|
6
|
+
from wbcore.contrib.permission.filters import ObjectPermissionsFilter
|
|
7
7
|
from wbcore.viewsets.mixins import DjangoFilterBackend
|
|
8
8
|
|
|
9
9
|
from ..models import Geography
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
from django.db.models.signals import post_migrate
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PermissionAppConfig(AppConfig):
|
|
6
|
+
name = "wbcore.contrib.permission"
|
|
7
|
+
label = "wbcore_permission"
|
|
8
|
+
|
|
9
|
+
def ready(self):
|
|
10
|
+
from wbcore.contrib.permission.management import refresh_internal_users
|
|
11
|
+
|
|
12
|
+
post_migrate.connect(
|
|
13
|
+
refresh_internal_users,
|
|
14
|
+
dispatch_uid="permission.refresh_internal_users",
|
|
15
|
+
)
|
|
@@ -11,13 +11,13 @@ class ObjectPermissionsFilter(BaseFilterBackend):
|
|
|
11
11
|
def filter_queryset(self, request, queryset, view):
|
|
12
12
|
from guardian.shortcuts import get_objects_for_user
|
|
13
13
|
|
|
14
|
-
from wbcore.contrib.
|
|
14
|
+
from wbcore.contrib.permission.models.mixins import PermissionObjectModelMixin
|
|
15
15
|
|
|
16
16
|
model_class = queryset.model
|
|
17
17
|
if issubclass(model_class, PermissionObjectModelMixin):
|
|
18
18
|
user = request.user
|
|
19
19
|
protected_objects = get_objects_for_user(
|
|
20
|
-
user, [model_class.view_perm_str], queryset, **model_class.
|
|
20
|
+
user, [model_class.view_perm_str], queryset, **model_class.permission_shortcut_kwargs
|
|
21
21
|
)
|
|
22
22
|
public_objects = queryset.filter(permission_type=PermissionObjectModelMixin.PermissionType.PUBLIC)
|
|
23
23
|
return queryset.filter(
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib.auth.models import Group
|
|
3
|
+
from django.db.models import Q, QuerySet
|
|
4
|
+
from django.utils.module_loading import import_string
|
|
5
|
+
|
|
6
|
+
from wbcore.contrib.authentication.models import User
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UserBackendRegistry:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
internal_users_backend_path = getattr(
|
|
12
|
+
settings, "USER_BACKEND", "wbcore.contrib.permission.internal.backend.UserBackend"
|
|
13
|
+
)
|
|
14
|
+
internal_users_backend_class = import_string(internal_users_backend_path)
|
|
15
|
+
self.backend = internal_users_backend_class()
|
|
16
|
+
|
|
17
|
+
def get_internal_groups(self) -> QuerySet[Group]:
|
|
18
|
+
return self.backend.get_internal_groups()
|
|
19
|
+
|
|
20
|
+
def get_internal_users(self) -> QuerySet[User]:
|
|
21
|
+
return User.objects.filter(Q(is_internal=True) | Q(id__in=self.backend.get_internal_users().values("id")))
|
|
22
|
+
|
|
23
|
+
def refresh_users(self, reset_all: bool = False):
|
|
24
|
+
if reset_all:
|
|
25
|
+
User.objects.filter(is_internal=True).update(is_internal=False)
|
|
26
|
+
users = []
|
|
27
|
+
for user in self.get_internal_users().filter(is_active=True):
|
|
28
|
+
user.is_internal = True
|
|
29
|
+
users.append(user)
|
|
30
|
+
User.objects.bulk_update(users, ["is_internal"])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_internal_groups() -> QuerySet[Group]:
|
|
34
|
+
"""
|
|
35
|
+
Return the cached groups of internals users
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A queryset of group corresponding to the internal notion defined by the set UserBackend
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If user backend path does not correspond to a valid module
|
|
42
|
+
"""
|
|
43
|
+
return UserBackendRegistry().internal_groups
|
|
@@ -9,12 +9,82 @@ from django.dispatch import receiver
|
|
|
9
9
|
from django.utils.translation import gettext_lazy as _
|
|
10
10
|
|
|
11
11
|
from wbcore.contrib.authentication.models.users import User
|
|
12
|
-
from wbcore.contrib.
|
|
13
|
-
from wbcore.permissions.mixins import PermissionMixin
|
|
12
|
+
from wbcore.contrib.permission.utils import reload_permissions
|
|
14
13
|
from wbcore.utils.itertools import get_inheriting_subclasses
|
|
15
14
|
from wbcore.workers import Queue
|
|
16
15
|
|
|
17
16
|
|
|
17
|
+
class PermissionMixin(models.Model):
|
|
18
|
+
class Meta:
|
|
19
|
+
abstract = True
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
@property
|
|
23
|
+
def view_perm_str(cls) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Get the view string permission identifer
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The view string permission identifier
|
|
29
|
+
"""
|
|
30
|
+
return f"{cls._meta.app_label}.view_{cls._meta.model_name}"
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
@property
|
|
34
|
+
def change_perm_str(cls) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Get the change string permission identifer
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
The change string permission identifier
|
|
40
|
+
"""
|
|
41
|
+
return f"{cls._meta.app_label}.change_{cls._meta.model_name}"
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
@property
|
|
45
|
+
def delete_perm_str(cls) -> str:
|
|
46
|
+
"""
|
|
47
|
+
Get the delete string permission identifer
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The delete string permission identifier
|
|
51
|
+
"""
|
|
52
|
+
return f"{cls._meta.app_label}.delete_{cls._meta.model_name}"
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
@property
|
|
56
|
+
def select_perm_str(cls) -> str:
|
|
57
|
+
"""
|
|
58
|
+
Get the select string permission identifer
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
The select string permission identifier
|
|
62
|
+
"""
|
|
63
|
+
return f"{cls._meta.app_label}.select_{cls._meta.model_name}"
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
@property
|
|
67
|
+
def admin_perm_str(cls) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Get the admin string permission identifer
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
The admin string permission identifier
|
|
73
|
+
"""
|
|
74
|
+
return f"{cls._meta.app_label}.administrate_{cls._meta.model_name}"
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
@property
|
|
78
|
+
def add_perm_str(cls) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Get the add string permission identifer
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The add string permission identifier
|
|
84
|
+
"""
|
|
85
|
+
return f"{cls._meta.app_label}.add_{cls._meta.model_name}"
|
|
86
|
+
|
|
87
|
+
|
|
18
88
|
class PermissionObjectModelMixin(PermissionMixin):
|
|
19
89
|
"""
|
|
20
90
|
Permission object Mixin that set the default behavior for permission assignments.
|
|
@@ -28,7 +98,7 @@ class PermissionObjectModelMixin(PermissionMixin):
|
|
|
28
98
|
PUBLIC = "PUBLIC", _("Public")
|
|
29
99
|
PRIVATE = "PRIVATE", _("Private")
|
|
30
100
|
|
|
31
|
-
|
|
101
|
+
permission_shortcut_kwargs = {"accept_global_perms": False}
|
|
32
102
|
|
|
33
103
|
permission_type = models.CharField(default=PermissionType.PRIVATE, choices=PermissionType.choices, max_length=8)
|
|
34
104
|
creator = models.ForeignKey(
|
|
@@ -3,8 +3,6 @@ from rest_framework.permissions import IsAuthenticated
|
|
|
3
3
|
|
|
4
4
|
from wbcore.enums import WidgetType
|
|
5
5
|
|
|
6
|
-
from .shortcuts import is_internal_user
|
|
7
|
-
|
|
8
6
|
|
|
9
7
|
class RestAPIModelPermissions(permissions.DjangoModelPermissions):
|
|
10
8
|
"""
|
|
@@ -42,7 +40,7 @@ class RestAPIModelPermissions(permissions.DjangoModelPermissions):
|
|
|
42
40
|
|
|
43
41
|
class IsInternalUser(IsAuthenticated):
|
|
44
42
|
def has_permission(self, request, view) -> bool:
|
|
45
|
-
return
|
|
43
|
+
return request.user.is_internal or request.user.is_superuser
|
|
46
44
|
|
|
47
45
|
|
|
48
46
|
class InternalUserPermissionMixin:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from celery import shared_task
|
|
2
2
|
|
|
3
|
-
from wbcore.contrib.
|
|
3
|
+
from wbcore.contrib.permission.models.mixins import PermissionObjectModelMixin
|
|
4
4
|
from wbcore.utils.itertools import get_inheriting_subclasses
|
|
5
5
|
from wbcore.workers import Queue
|
|
6
6
|
|
|
@@ -2,21 +2,19 @@ import pytest
|
|
|
2
2
|
from django.contrib.auth.models import Permission
|
|
3
3
|
from django.contrib.contenttypes.models import ContentType
|
|
4
4
|
from faker import Faker
|
|
5
|
+
|
|
5
6
|
from wbcore.contrib.authentication.models.users import User
|
|
6
|
-
from wbcore.
|
|
7
|
+
from wbcore.contrib.permission.internal.registry import UserBackendRegistry
|
|
7
8
|
|
|
8
9
|
fake = Faker()
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def create_internal_user():
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
user = User.objects.create(is_active=True, username=fake.user_name(), email=fake.email())
|
|
13
|
+
user = User.objects.create(is_active=True, username=fake.user_name(), email=fake.email(), is_internal=True)
|
|
15
14
|
permission = Permission.objects.get_or_create(
|
|
16
15
|
content_type=ContentType.objects.get_for_model(User), codename="is_internal_user"
|
|
17
16
|
)[0]
|
|
18
17
|
user.user_permissions.add(permission)
|
|
19
|
-
user_registry.reset_cache()
|
|
20
18
|
return user
|
|
21
19
|
|
|
22
20
|
|
|
@@ -24,4 +22,4 @@ def create_internal_user():
|
|
|
24
22
|
class TestBackend:
|
|
25
23
|
def test_get_internal_users(self):
|
|
26
24
|
user = create_internal_user()
|
|
27
|
-
assert set(get_internal_users()) == {user}
|
|
25
|
+
assert set(UserBackendRegistry().get_internal_users()) == {user}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
-
from wbcore.contrib.
|
|
3
|
+
from wbcore.contrib.permission.models.mixins import PermissionObjectModelMixin
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
@pytest.fixture
|
|
@@ -21,8 +21,8 @@ def mocked_permission_object_model_mixin(mocker):
|
|
|
21
21
|
class TestPermissionObjectModelMixin:
|
|
22
22
|
def test_save_run_assign_permissions(self, mocker, mocked_permission_object_model_mixin):
|
|
23
23
|
mocker.patch("django.db.models.Model.save")
|
|
24
|
-
on_commit = mocker.patch("wbcore.contrib.
|
|
25
|
-
content_type_class = mocker.patch("wbcore.contrib.
|
|
24
|
+
on_commit = mocker.patch("wbcore.contrib.permission.models.mixins.transaction.on_commit")
|
|
25
|
+
content_type_class = mocker.patch("wbcore.contrib.permission.models.mixins.ContentType")
|
|
26
26
|
content_type_class.objects.get_for_model.return_value = mocker.Mock()
|
|
27
27
|
|
|
28
28
|
mocked_permission_object_model_mixin().save()
|
|
@@ -30,7 +30,7 @@ class TestPermissionObjectModelMixin:
|
|
|
30
30
|
on_commit.assert_called_once()
|
|
31
31
|
|
|
32
32
|
def test_reload_permissions(self, mocker, mocked_permission_object_model_mixin):
|
|
33
|
-
reload_permissions = mocker.patch("wbcore.contrib.
|
|
33
|
+
reload_permissions = mocker.patch("wbcore.contrib.permission.models.mixins.reload_permissions")
|
|
34
34
|
mocked_permission_object_model_mixin().reload_permissions(prune_existing=True, force_pruning=True)
|
|
35
35
|
reload_permissions.assert_called_once()
|
|
36
36
|
|
|
@@ -79,7 +79,7 @@ class TestPermissionObjectModelMixin:
|
|
|
79
79
|
|
|
80
80
|
@pytest.mark.django_db
|
|
81
81
|
def test_post_save_user(user_factory, mocker):
|
|
82
|
-
on_commit = mocker.patch("wbcore.contrib.
|
|
82
|
+
on_commit = mocker.patch("wbcore.contrib.permission.models.mixins.transaction.on_commit")
|
|
83
83
|
user_factory.create()
|
|
84
84
|
|
|
85
85
|
on_commit.assert_called_once()
|
|
@@ -88,6 +88,6 @@ def test_post_save_user(user_factory, mocker):
|
|
|
88
88
|
@pytest.mark.django_db
|
|
89
89
|
def test_post_save_user_not_called(user_factory, mocker):
|
|
90
90
|
user_factory.create()
|
|
91
|
-
on_commit = mocker.patch("wbcore.contrib.
|
|
91
|
+
on_commit = mocker.patch("wbcore.contrib.permission.models.mixins.transaction.on_commit")
|
|
92
92
|
|
|
93
93
|
on_commit.assert_not_called()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from django.contrib.contenttypes.models import ContentType
|
|
3
3
|
|
|
4
|
-
from wbcore.contrib.
|
|
4
|
+
from wbcore.contrib.permission.models.mixins import (
|
|
5
5
|
assign_object_permissions_for_user_as_task,
|
|
6
6
|
assign_user_permissions_for_object_as_task,
|
|
7
7
|
)
|
|
@@ -9,7 +9,7 @@ from wbcore.contrib.guardian.models.mixins import (
|
|
|
9
9
|
|
|
10
10
|
class TestAssignUserPermissionsForObjectAsTask:
|
|
11
11
|
def test_called(self, mocker):
|
|
12
|
-
content_type_class = mocker.patch("wbcore.contrib.
|
|
12
|
+
content_type_class = mocker.patch("wbcore.contrib.permission.models.mixins.ContentType")
|
|
13
13
|
content_type = mocker.Mock()
|
|
14
14
|
model_class = mocker.Mock()
|
|
15
15
|
instance = mocker.Mock()
|
|
@@ -22,7 +22,7 @@ class TestAssignUserPermissionsForObjectAsTask:
|
|
|
22
22
|
instance.reload_permissions.assert_called_once_with(prune_existing=True)
|
|
23
23
|
|
|
24
24
|
def test_called_with_prune_existing(self, mocker):
|
|
25
|
-
content_type_class = mocker.patch("wbcore.contrib.
|
|
25
|
+
content_type_class = mocker.patch("wbcore.contrib.permission.models.mixins.ContentType")
|
|
26
26
|
content_type = mocker.Mock()
|
|
27
27
|
model_class = mocker.Mock()
|
|
28
28
|
instance = mocker.Mock()
|
|
@@ -35,7 +35,7 @@ class TestAssignUserPermissionsForObjectAsTask:
|
|
|
35
35
|
instance.reload_permissions.assert_called_once_with(prune_existing=False)
|
|
36
36
|
|
|
37
37
|
def test_not_called_without_model_class(self, mocker):
|
|
38
|
-
content_type_class = mocker.patch("wbcore.contrib.
|
|
38
|
+
content_type_class = mocker.patch("wbcore.contrib.permission.models.mixins.ContentType")
|
|
39
39
|
content_type = mocker.Mock()
|
|
40
40
|
model_class = mocker.Mock()
|
|
41
41
|
instance = mocker.Mock()
|
|
@@ -64,14 +64,14 @@ class TestAssignUserPermissionsForObjectAsTask:
|
|
|
64
64
|
|
|
65
65
|
class TestAssignObjectPermissionsForUserAsTask:
|
|
66
66
|
def test_called(self, mocker):
|
|
67
|
-
user_class = mocker.patch("wbcore.contrib.
|
|
67
|
+
user_class = mocker.patch("wbcore.contrib.permission.models.mixins.User")
|
|
68
68
|
user = mocker.Mock()
|
|
69
69
|
|
|
70
70
|
user_class.objects.get.return_value = user
|
|
71
71
|
|
|
72
|
-
get_inheriting_subclasses = mocker.patch("wbcore.contrib.
|
|
72
|
+
get_inheriting_subclasses = mocker.patch("wbcore.contrib.permission.models.mixins.get_inheriting_subclasses")
|
|
73
73
|
get_inheriting_subclasses.return_value = [user_class]
|
|
74
|
-
reload_permissions = mocker.patch("wbcore.contrib.
|
|
74
|
+
reload_permissions = mocker.patch("wbcore.contrib.permission.models.mixins.reload_permissions")
|
|
75
75
|
|
|
76
76
|
assign_object_permissions_for_user_as_task(user_id=1)
|
|
77
77
|
|
|
@@ -2,9 +2,9 @@ import pytest
|
|
|
2
2
|
from django.contrib.contenttypes.models import ContentType
|
|
3
3
|
|
|
4
4
|
from wbcore.contrib.authentication.models.users import Permission
|
|
5
|
-
from wbcore.contrib.
|
|
6
|
-
from wbcore.contrib.
|
|
7
|
-
from wbcore.contrib.
|
|
5
|
+
from wbcore.contrib.permission.models import UserObjectPermission
|
|
6
|
+
from wbcore.contrib.permission.models.mixins import PermissionObjectModelMixin
|
|
7
|
+
from wbcore.contrib.permission.utils import (
|
|
8
8
|
assign_permissions,
|
|
9
9
|
get_permission_matrix,
|
|
10
10
|
get_public_user_or_group,
|
|
@@ -42,7 +42,7 @@ class TestAssignPermissionsMap:
|
|
|
42
42
|
).editable
|
|
43
43
|
|
|
44
44
|
def test_assign_permission_with_non_perm(self, mocker):
|
|
45
|
-
assign_perm = mocker.patch("wbcore.contrib.
|
|
45
|
+
assign_perm = mocker.patch("wbcore.contrib.permission.utils.assign_perm")
|
|
46
46
|
assign_perm.return_value = None
|
|
47
47
|
|
|
48
48
|
assign_permissions([("view_user", mocker.Mock(), mocker.Mock(), True)])
|
|
@@ -163,8 +163,8 @@ class TestPrunePermissions:
|
|
|
163
163
|
|
|
164
164
|
class TestReloadPermissions:
|
|
165
165
|
def test_reload_permissions(self, mocker):
|
|
166
|
-
get_permission_matrix = mocker.patch("wbcore.contrib.
|
|
167
|
-
assign_permissions = mocker.patch("wbcore.contrib.
|
|
166
|
+
get_permission_matrix = mocker.patch("wbcore.contrib.permission.utils.get_permission_matrix")
|
|
167
|
+
assign_permissions = mocker.patch("wbcore.contrib.permission.utils.assign_permissions")
|
|
168
168
|
|
|
169
169
|
queryset = mocker.Mock()
|
|
170
170
|
|
|
@@ -174,9 +174,9 @@ class TestReloadPermissions:
|
|
|
174
174
|
assign_permissions.assert_called_once()
|
|
175
175
|
|
|
176
176
|
def test_reload_permissions_with_prune(self, mocker):
|
|
177
|
-
prune_permissions = mocker.patch("wbcore.contrib.
|
|
178
|
-
mocker.patch("wbcore.contrib.
|
|
179
|
-
mocker.patch("wbcore.contrib.
|
|
177
|
+
prune_permissions = mocker.patch("wbcore.contrib.permission.utils.prune_permissions")
|
|
178
|
+
mocker.patch("wbcore.contrib.permission.utils.get_permission_matrix")
|
|
179
|
+
mocker.patch("wbcore.contrib.permission.utils.assign_permissions")
|
|
180
180
|
|
|
181
181
|
queryset = mocker.Mock()
|
|
182
182
|
|
|
@@ -185,9 +185,9 @@ class TestReloadPermissions:
|
|
|
185
185
|
prune_permissions.assert_not_called()
|
|
186
186
|
|
|
187
187
|
def test_reload_permissions_with_prune_and_instance(self, mocker):
|
|
188
|
-
prune_permissions = mocker.patch("wbcore.contrib.
|
|
189
|
-
mocker.patch("wbcore.contrib.
|
|
190
|
-
mocker.patch("wbcore.contrib.
|
|
188
|
+
prune_permissions = mocker.patch("wbcore.contrib.permission.utils.prune_permissions")
|
|
189
|
+
mocker.patch("wbcore.contrib.permission.utils.get_permission_matrix")
|
|
190
|
+
mocker.patch("wbcore.contrib.permission.utils.assign_permissions")
|
|
191
191
|
|
|
192
192
|
queryset = mocker.Mock()
|
|
193
193
|
instance = mocker.Mock()
|
|
@@ -4,12 +4,12 @@ from django.db.models import Q
|
|
|
4
4
|
from guardian.shortcuts import assign_perm
|
|
5
5
|
|
|
6
6
|
from wbcore.contrib.authentication.models.users import Permission
|
|
7
|
-
from wbcore.contrib.
|
|
7
|
+
from wbcore.contrib.permission.viewsets import PivotUserObjectPermissionModelViewSet
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class TestPivotUserObjectPermissionModelViewSet:
|
|
11
11
|
def test_cached_property_permissions(self, mocker, request):
|
|
12
|
-
mocked_filter = mocker.patch("wbcore.contrib.
|
|
12
|
+
mocked_filter = mocker.patch("wbcore.contrib.permission.viewsets.viewsets.Permission.objects.filter")
|
|
13
13
|
view = PivotUserObjectPermissionModelViewSet(request=request, kwargs={"content_type_id": 1})
|
|
14
14
|
assert view.permissions
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ class TestPivotUserObjectPermissionModelViewSet:
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
def test_linked_object(self, mocker, request):
|
|
24
|
-
mocked_contrib_type = mocker.patch("wbcore.contrib.
|
|
24
|
+
mocked_contrib_type = mocker.patch("wbcore.contrib.permission.viewsets.viewsets.ContentType.objects")
|
|
25
25
|
mocked_get_object_for_this_type = mocker.Mock()
|
|
26
26
|
mocked_contrib_type.get.return_value = mocked_get_object_for_this_type
|
|
27
27
|
view = PivotUserObjectPermissionModelViewSet(request=request, kwargs={"content_type_id": 1, "object_pk": 1})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from django.urls import include, path
|
|
2
2
|
|
|
3
|
-
from wbcore.contrib.
|
|
3
|
+
from wbcore.contrib.permission.viewsets import PivotUserObjectPermissionModelViewSet
|
|
4
4
|
from wbcore.routers import WBCoreRouter
|
|
5
5
|
|
|
6
6
|
router = WBCoreRouter()
|
|
@@ -10,11 +10,37 @@ from guardian.shortcuts import assign_perm, get_anonymous_user
|
|
|
10
10
|
from psycopg.errors import InvalidCursorName
|
|
11
11
|
|
|
12
12
|
from wbcore.contrib.authentication.models import User
|
|
13
|
-
from wbcore.contrib.
|
|
14
|
-
from wbcore.permissions.shortcuts import get_internal_users
|
|
13
|
+
from wbcore.contrib.permission.models import UserObjectPermission
|
|
15
14
|
|
|
16
15
|
if TYPE_CHECKING:
|
|
17
|
-
from wbcore.contrib.
|
|
16
|
+
from wbcore.contrib.permission.models.mixins import PermissionObjectModelMixin
|
|
17
|
+
|
|
18
|
+
from django.contrib.auth.models import Permission
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def perm_to_permission(perm: str) -> Permission:
|
|
22
|
+
"""
|
|
23
|
+
Convert a identifier string permission format in 'app_label.codename'
|
|
24
|
+
(teremd as *perm*) to a django permission instance.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
perm: The string permission identifier in the form 'app_label.codename'
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The Permission object corresponding to the given string identifier
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
AttributeError: When the given string identifier does not match the expected format 'app_label.codename'
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
app_label, codename = perm.split(".", 1)
|
|
37
|
+
except IndexError as e:
|
|
38
|
+
raise AttributeError(
|
|
39
|
+
"The format of identifier string permission (perm) is wrong. It should be in 'app_label.codename'."
|
|
40
|
+
) from e
|
|
41
|
+
else:
|
|
42
|
+
permission = Permission.objects.get(content_type__app_label=app_label, codename=codename)
|
|
43
|
+
return permission
|
|
18
44
|
|
|
19
45
|
|
|
20
46
|
def assign_permissions(permissions_map: Iterable[tuple[str, Model, User, bool]]):
|
|
@@ -51,7 +77,9 @@ def get_public_user_or_group(only_internal: bool = False) -> QuerySet[User]:
|
|
|
51
77
|
users = User.objects.filter(is_active=True)
|
|
52
78
|
if only_internal:
|
|
53
79
|
users = users.filter(
|
|
54
|
-
Q(is_superuser=True)
|
|
80
|
+
Q(is_superuser=True)
|
|
81
|
+
| Q(id=get_anonymous_user().pk)
|
|
82
|
+
| Q(id__in=User.objects.filter_internal().values("id"))
|
|
55
83
|
)
|
|
56
84
|
return users
|
|
57
85
|
|
|
@@ -3,8 +3,8 @@ from contextlib import suppress
|
|
|
3
3
|
from django.dispatch import receiver
|
|
4
4
|
from rest_framework.reverse import reverse
|
|
5
5
|
|
|
6
|
-
from wbcore.contrib.guardian.models.mixins import PermissionObjectModelMixin
|
|
7
6
|
from wbcore.contrib.icons.icons import WBIcon
|
|
7
|
+
from wbcore.contrib.permission.models.mixins import PermissionObjectModelMixin
|
|
8
8
|
from wbcore.metadata.configs.buttons import ButtonViewConfig
|
|
9
9
|
from wbcore.metadata.configs.buttons.buttons import WidgetButton
|
|
10
10
|
from wbcore.metadata.configs.buttons.enums import Button
|
|
@@ -30,7 +30,7 @@ def add_object_permission_button(sender, instance, request, view, pk=None, **kwa
|
|
|
30
30
|
and pk is not None
|
|
31
31
|
):
|
|
32
32
|
endpoint = reverse(
|
|
33
|
-
"wbcore:
|
|
33
|
+
"wbcore:permission:pivoteduserobjectpermission-list",
|
|
34
34
|
args=[view.get_content_type().id, pk],
|
|
35
35
|
request=request,
|
|
36
36
|
)
|
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
from wbcore.metadata.configs import display as dp
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
|
-
from wbcore.contrib.
|
|
6
|
+
from wbcore.contrib.permission.viewsets import PivotUserObjectPermissionModelViewSet
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class PivotUserObjectPermissionDisplayViewConfig(dp.DisplayViewConfig):
|
|
@@ -6,21 +6,21 @@ from wbcore.metadata.configs.endpoints import EndpointViewConfig
|
|
|
6
6
|
class PivotUserObjectPermissionEndpointViewConfig(EndpointViewConfig):
|
|
7
7
|
def get_instance_endpoint(self, **kwargs):
|
|
8
8
|
return reverse(
|
|
9
|
-
"wbcore:
|
|
9
|
+
"wbcore:permission:pivoteduserobjectpermission-list",
|
|
10
10
|
args=[self.view.kwargs.get("content_type_id", None), self.view.kwargs.get("object_pk", None)],
|
|
11
11
|
request=self.request,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
def get_update_endpoint(self, **kwargs):
|
|
15
15
|
return reverse(
|
|
16
|
-
"wbcore:
|
|
16
|
+
"wbcore:permission:pivoteduserobjectpermission-list",
|
|
17
17
|
args=[self.view.kwargs.get("content_type_id", None), self.view.kwargs.get("object_pk", None)],
|
|
18
18
|
request=self.request,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
def get_create_endpoint(self, **kwargs):
|
|
22
22
|
return reverse(
|
|
23
|
-
"wbcore:
|
|
23
|
+
"wbcore:permission:pivoteduserobjectpermission-list",
|
|
24
24
|
args=[self.view.kwargs.get("content_type_id", None), self.view.kwargs.get("object_pk", None)],
|
|
25
25
|
request=self.request,
|
|
26
26
|
)
|
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
from wbcore.metadata.configs.titles import TitleViewConfig
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
|
-
from wbcore.contrib.
|
|
6
|
+
from wbcore.contrib.permission.viewsets import PivotUserObjectPermissionModelViewSet
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class PivotUserObjectPermissionTitleViewConfig(TitleViewConfig):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from wbcore.contrib.
|
|
1
|
+
from wbcore.contrib.permission.filters import ObjectPermissionsFilter
|
|
2
2
|
from wbcore.viewsets.mixins import FilterMixin
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
class ObjectPermissionFilterMixin(FilterMixin):
|
|
6
6
|
filter_backends = (ObjectPermissionsFilter, *FilterMixin.filter_backends)
|
|
@@ -7,8 +7,8 @@ from rest_framework.exceptions import ValidationError
|
|
|
7
7
|
from wbcore import serializers, viewsets
|
|
8
8
|
from wbcore.contrib.authentication.models.users import Permission, User
|
|
9
9
|
from wbcore.contrib.authentication.serializers import UserRepresentationSerializer
|
|
10
|
-
from wbcore.contrib.
|
|
11
|
-
from wbcore.contrib.
|
|
10
|
+
from wbcore.contrib.permission.models import UserObjectPermission
|
|
11
|
+
from wbcore.contrib.permission.viewsets.configs import (
|
|
12
12
|
PivotUserObjectPermissionButtonViewConfig,
|
|
13
13
|
PivotUserObjectPermissionDisplayViewConfig,
|
|
14
14
|
PivotUserObjectPermissionEndpointViewConfig,
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
from django.utils.translation import gettext as _
|
|
2
2
|
|
|
3
3
|
from wbcore.menus import ItemPermission, MenuItem
|
|
4
|
-
from wbcore.permissions.shortcuts import is_internal_user
|
|
5
4
|
|
|
6
5
|
CONDITION_MENUITEM = MenuItem(
|
|
7
6
|
label=_("Condition"),
|
|
8
7
|
endpoint="wbcore:workflow:condition-list",
|
|
9
8
|
permission=ItemPermission(
|
|
10
|
-
method=lambda request:
|
|
9
|
+
method=lambda request: request.user.is_internal, permissions=["workflow.view_condition"]
|
|
11
10
|
),
|
|
12
11
|
add=MenuItem(
|
|
13
12
|
label=_("Create Condition"),
|
|
14
13
|
endpoint="wbcore:workflow:condition-list",
|
|
15
14
|
permission=ItemPermission(
|
|
16
|
-
method=lambda request:
|
|
15
|
+
method=lambda request: request.user.is_internal, permissions=["workflow.add_condition"]
|
|
17
16
|
),
|
|
18
17
|
),
|
|
19
18
|
)
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
from django.utils.translation import gettext as _
|
|
2
2
|
|
|
3
3
|
from wbcore.menus import ItemPermission, MenuItem
|
|
4
|
-
from wbcore.permissions.shortcuts import is_internal_user
|
|
5
4
|
|
|
6
5
|
DATA_MENUITEM = MenuItem(
|
|
7
6
|
label=_("Data"),
|
|
8
7
|
endpoint="wbcore:workflow:data-list",
|
|
9
|
-
permission=ItemPermission(
|
|
10
|
-
method=lambda request: is_internal_user(request.user), permissions=["workflow.view_data"]
|
|
11
|
-
),
|
|
8
|
+
permission=ItemPermission(method=lambda request: request.user.is_internal, permissions=["workflow.view_data"]),
|
|
12
9
|
add=MenuItem(
|
|
13
10
|
label=_("Create Data"),
|
|
14
11
|
endpoint="wbcore:workflow:data-list",
|
|
15
|
-
permission=ItemPermission(
|
|
16
|
-
method=lambda request: is_internal_user(request.user), permissions=["workflow.add_data"]
|
|
17
|
-
),
|
|
12
|
+
permission=ItemPermission(method=lambda request: request.user.is_internal, permissions=["workflow.add_data"]),
|
|
18
13
|
),
|
|
19
14
|
)
|