GeneralManager 0.10.6__py3-none-any.whl → 0.11.0__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.
@@ -1,5 +1,14 @@
1
1
  import inspect
2
- from typing import get_type_hints, Optional, Union, List, Tuple, get_origin, get_args
2
+ from typing import (
3
+ get_type_hints,
4
+ Optional,
5
+ Union,
6
+ List,
7
+ Tuple,
8
+ get_origin,
9
+ get_args,
10
+ Type,
11
+ )
3
12
  import graphene
4
13
 
5
14
  from general_manager.api.graphql import GraphQL
@@ -7,9 +16,10 @@ from general_manager.manager.generalManager import GeneralManager
7
16
 
8
17
  from general_manager.utils.formatString import snake_to_camel
9
18
  from typing import TypeAliasType
19
+ from general_manager.permission.mutationPermission import MutationPermission
10
20
 
11
21
 
12
- def graphQlMutation(needs_role: Optional[str] = None, auth_required: bool = False):
22
+ def graphQlMutation(permission: Optional[Type[MutationPermission]] = None):
13
23
  """
14
24
  Decorator that transforms a function into a GraphQL mutation class and registers it for use in a Graphene-based API.
15
25
 
@@ -109,11 +119,9 @@ def graphQlMutation(needs_role: Optional[str] = None, auth_required: bool = Fals
109
119
 
110
120
  # Define mutate method
111
121
  def _mutate(root, info, **kwargs):
112
- # Auth check
113
- if auth_required and not getattr(info.context, "user", None):
114
- return mutation_class(
115
- **{"success": False, "errors": ["Authentication required"]}
116
- )
122
+
123
+ if permission:
124
+ permission.check(kwargs, info.context.user)
117
125
  try:
118
126
  result = fn(info, **kwargs)
119
127
  data = {}
@@ -8,6 +8,7 @@ from general_manager.permission.permissionChecks import (
8
8
 
9
9
  from django.contrib.auth.models import AnonymousUser, AbstractUser
10
10
  from general_manager.permission.permissionDataManager import PermissionDataManager
11
+ from general_manager.permission.utils import validatePermissionString
11
12
 
12
13
  if TYPE_CHECKING:
13
14
  from general_manager.manager.generalManager import GeneralManager
@@ -153,24 +154,9 @@ class BasePermission(ABC):
153
154
  self,
154
155
  permission: str,
155
156
  ) -> bool:
156
- # permission can be a combination of multiple permissions
157
- # separated by "&" (e.g. "isAuthenticated&isMatchingKeyAccount")
158
- # this means that all sub_permissions must be true
159
- return all(
160
- [
161
- self.__validateSinglePermission(sub_permission)
162
- for sub_permission in permission.split("&")
163
- ]
164
- )
165
-
166
- def __validateSinglePermission(
167
- self,
168
- permission: str,
169
- ) -> bool:
170
- permission_function, *config = permission.split(":")
171
- if permission_function not in permission_functions:
172
- raise ValueError(f"Permission {permission} not found")
173
-
174
- return permission_functions[permission_function]["permission_method"](
175
- self.instance, self.request_user, config
176
- )
157
+ """
158
+ Validates a permission string which can be a combination of multiple permissions
159
+ separated by "&" (e.g. "isAuthenticated&isMatchingKeyAccount").
160
+ This means that all sub_permissions must be true.
161
+ """
162
+ return validatePermissionString(permission, self.instance, self.request_user)
@@ -30,8 +30,7 @@ class ManagerBasedPermission(BasePermission):
30
30
  request_user: AbstractUser,
31
31
  ) -> None:
32
32
 
33
- self.__instance = instance
34
- self.__request_user = request_user
33
+ super().__init__(instance, request_user)
35
34
  self.__attribute_permissions = self.__getAttributePermissions()
36
35
  self.__based_on_permission = self.__getBasedOnPermission()
37
36
  self.__overall_results: Dict[permission_type, Optional[bool]] = {
@@ -51,7 +50,7 @@ class ManagerBasedPermission(BasePermission):
51
50
  basis_object = getattr(self.instance, __based_on__, None)
52
51
  if basis_object is None:
53
52
  raise ValueError(
54
- f"Based on object {__based_on__} not found in instance {self.__instance}"
53
+ f"Based on object {__based_on__} not found in instance {self.instance}"
55
54
  )
56
55
  if not isinstance(basis_object, GeneralManager) and not issubclass(
57
56
  basis_object, GeneralManager
@@ -71,14 +70,6 @@ class ManagerBasedPermission(BasePermission):
71
70
  request_user=self.request_user,
72
71
  )
73
72
 
74
- @property
75
- def instance(self) -> PermissionDataManager | GeneralManager:
76
- return self.__instance
77
-
78
- @property
79
- def request_user(self) -> AbstractUser:
80
- return self.__request_user
81
-
82
73
  def __getAttributePermissions(
83
74
  self,
84
75
  ) -> dict[str, dict[permission_type, list[str]]]:
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+ from django.contrib.auth.models import AbstractUser, AnonymousUser
3
+ from typing import Any
4
+ from general_manager.permission.basePermission import BasePermission
5
+
6
+ from general_manager.permission.permissionDataManager import PermissionDataManager
7
+ from general_manager.permission.utils import validatePermissionString
8
+
9
+
10
+ class MutationPermission:
11
+ __mutate__: list[str]
12
+
13
+ def __init__(
14
+ self, data: dict[str, Any], request_user: AbstractUser | AnonymousUser
15
+ ) -> None:
16
+ self._data = PermissionDataManager(data)
17
+ self._request_user = request_user
18
+ self.__attribute_permissions = self.__getAttributePermissions()
19
+
20
+ self.__overall_result: bool | None = None
21
+
22
+ @property
23
+ def data(self) -> PermissionDataManager:
24
+ return self._data
25
+
26
+ @property
27
+ def request_user(self) -> AbstractUser | AnonymousUser:
28
+ return self._request_user
29
+
30
+ def __getAttributePermissions(
31
+ self,
32
+ ) -> dict[str, list[str]]:
33
+ attribute_permissions = {}
34
+ for attribute in self.__class__.__dict__:
35
+ if not attribute.startswith("__"):
36
+ attribute_permissions[attribute] = getattr(self.__class__, attribute)
37
+ return attribute_permissions
38
+
39
+ @classmethod
40
+ def check(
41
+ cls,
42
+ data: dict[str, Any],
43
+ request_user: AbstractUser | AnonymousUser | Any,
44
+ ) -> None:
45
+ """
46
+ Check if the user has permission to perform the mutation based on the provided data.
47
+ Raises:
48
+ PermissionError: If the user does not have permission.
49
+ """
50
+ errors = []
51
+ Permission = cls(data, BasePermission.getUserWithId(request_user))
52
+ for key in data:
53
+ if not Permission.checkPermission(key):
54
+ errors.append(
55
+ f"Permission denied for {key} with value {data[key]} for user {request_user}"
56
+ )
57
+ if errors:
58
+ raise PermissionError(f"Permission denied with errors: {errors}")
59
+
60
+ def checkPermission(
61
+ self,
62
+ attribute: str,
63
+ ) -> bool:
64
+
65
+ has_attribute_permissions = attribute in self.__attribute_permissions
66
+
67
+ if not has_attribute_permissions:
68
+ last_result = self.__overall_result
69
+ if last_result is not None:
70
+ return last_result
71
+ attribute_permission = True
72
+ else:
73
+ attribute_permission = self.__checkSpecificPermission(
74
+ self.__attribute_permissions[attribute]
75
+ )
76
+
77
+ permission = self.__checkSpecificPermission(self.__mutate__)
78
+ self.__overall_result = permission
79
+ return permission and attribute_permission
80
+
81
+ def __checkSpecificPermission(
82
+ self,
83
+ permissions: list[str],
84
+ ) -> bool:
85
+ for permission in permissions:
86
+ if validatePermissionString(permission, self.data, self.request_user):
87
+ return True
88
+ return False
@@ -24,10 +24,6 @@ class PermissionDataManager(Generic[GeneralManagerData]):
24
24
  self.getData = (
25
25
  lambda name, permission_data=permission_data: permission_data.get(name)
26
26
  )
27
- if manager is None:
28
- raise ValueError(
29
- "Manager must be provided if permission_data is a dict"
30
- )
31
27
  self._manager = manager
32
28
  else:
33
29
  raise TypeError(
@@ -48,7 +44,7 @@ class PermissionDataManager(Generic[GeneralManagerData]):
48
44
  return self._permission_data
49
45
 
50
46
  @property
51
- def manager(self) -> type[GeneralManagerData]:
47
+ def manager(self) -> type[GeneralManagerData] | None:
52
48
  return self._manager
53
49
 
54
50
  def __getattr__(self, name: str) -> Any:
@@ -0,0 +1,35 @@
1
+ from general_manager.permission.permissionChecks import (
2
+ permission_functions,
3
+ )
4
+ from general_manager.permission.permissionDataManager import PermissionDataManager
5
+ from django.contrib.auth.models import AbstractUser, AnonymousUser
6
+
7
+ from general_manager.manager.generalManager import GeneralManager
8
+ from general_manager.manager.meta import GeneralManagerMeta
9
+
10
+
11
+ def validatePermissionString(
12
+ permission: str,
13
+ data: PermissionDataManager | GeneralManager | GeneralManagerMeta,
14
+ request_user: AbstractUser | AnonymousUser,
15
+ ) -> bool:
16
+ # permission can be a combination of multiple permissions
17
+ # separated by "&" (e.g. "isAuthenticated&isMatchingKeyAccount")
18
+ # this means that all sub_permissions must be true
19
+ def _validateSinglePermission(
20
+ permission: str,
21
+ ) -> bool:
22
+ permission_function, *config = permission.split(":")
23
+ if permission_function not in permission_functions:
24
+ raise ValueError(f"Permission {permission} not found")
25
+
26
+ return permission_functions[permission_function]["permission_method"](
27
+ data, request_user, config
28
+ )
29
+
30
+ return all(
31
+ [
32
+ _validateSinglePermission(sub_permission)
33
+ for sub_permission in permission.split("&")
34
+ ]
35
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.10.6
3
+ Version: 0.11.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License-Expression: MIT
@@ -1,7 +1,7 @@
1
1
  general_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  general_manager/apps.py,sha256=ii-48klC2kBRjxtdQU9-RtPq66lZ8fDa7BmqyH-mbzk,9904
3
3
  general_manager/api/graphql.py,sha256=jpdtaep_ql94krsVvGJRpuqT-MR6iZCFpezeJAl4Mlw,33382
4
- general_manager/api/mutation.py,sha256=RvKp4OnV70q4Dqmu407Cd0FXgD12WdbRgzYCS4qd_bg,5990
4
+ general_manager/api/mutation.py,sha256=41BvkY4qYU_Kq2R6GUK14AnU7sSTydw4tSw1f4CAlxI,5954
5
5
  general_manager/api/property.py,sha256=oc93p1P8dcIvrNorRuqD1EJVsd6eYttYhZuAS0s28gs,696
6
6
  general_manager/bucket/baseBucket.py,sha256=UjEai7cVxgDJysoVY2-7s3YULW7bi-sx2mPqRKFWyTw,7728
7
7
  general_manager/bucket/calculationBucket.py,sha256=a41YVdfhxKf_gpWlRKjXYmS1YuO-6VC0hn60RyLKByU,18777
@@ -32,11 +32,13 @@ general_manager/measurement/__init__.py,sha256=X97meFujBldE5v0WMF7SmKeGpC5R0JTcz
32
32
  general_manager/measurement/measurement.py,sha256=T3R2qqDkqPY_91DzN0zJF4ug62aCNyN5-75WE9Tm7ko,16067
33
33
  general_manager/measurement/measurementField.py,sha256=ixkZR_t--HGckK2iYi6sXOOa_Vbni4QRxK5Ngmy5YKc,6863
34
34
  general_manager/permission/__init__.py,sha256=5UlDERN60Vn8obGVkT-cOM8kHjzmoxgK5w5FgTCDhGE,59
35
- general_manager/permission/basePermission.py,sha256=14iKo6qVmaUdg1sAz-gSZyNtpVKAAapIhutVAMDf93c,6056
35
+ general_manager/permission/basePermission.py,sha256=DeiAX2sQQhtdquO13jys2MSkp0kPdg2oo7PSqB9q5Bw,5653
36
36
  general_manager/permission/fileBasedPermission.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- general_manager/permission/managerBasedPermission.py,sha256=VgZJVgkXWdaLk6K7c0kYxTbELnTYK6UY9mMuKH5u64w,5728
37
+ general_manager/permission/managerBasedPermission.py,sha256=DmoSLZ5m9zOyqAotKFDUbr7dahhsvS3K63kI8ChURN0,5491
38
+ general_manager/permission/mutationPermission.py,sha256=tYeunZQOJiUkQIqW0lU0KafwJKaTkrHTYKi7zV0wqHI,2972
38
39
  general_manager/permission/permissionChecks.py,sha256=T-9khBqiwM4ASBdey9p07sC_xgzceIU9EAE0reukguM,1655
39
- general_manager/permission/permissionDataManager.py,sha256=Ji7fsnuaKTa6M8yzCGyzrIHyGa_ZvqJM7sXR97-uTrA,1937
40
+ general_manager/permission/permissionDataManager.py,sha256=EzF-XKJZKvKdNpF77ATYJ_VXSF09L-HTFe8h8p-JdB4,1784
41
+ general_manager/permission/utils.py,sha256=_cznQLk-wmK35pqs3dtgY6I94Ahyz_bTSmpkT0-MQnI,1277
40
42
  general_manager/rule/__init__.py,sha256=4Har5cfPD1fmOsilTDod-ZUz3Com-tkl58jz7yY4fD0,23
41
43
  general_manager/rule/handler.py,sha256=z8SFHTIZ0LbLh3fV56Mud0V4_OvWkqJjlHvFqau7Qfk,7334
42
44
  general_manager/rule/rule.py,sha256=3FVCKGL7BTVoStdgOTdWQwuoVRIxAIAilV4VOzouDpc,10759
@@ -49,8 +51,8 @@ general_manager/utils/makeCacheKey.py,sha256=UlFsxHXgsYy69AAelkF6GDvY4h7AImT2bBn
49
51
  general_manager/utils/noneToZero.py,sha256=KfQtMQnrT6vsYST0K7lv6pVujkDcK3XL8czHYOhgqKQ,539
50
52
  general_manager/utils/pathMapping.py,sha256=nrz5owQg2a69Yig1eCXorR9U0NSw7NmBAk5OkeoHTdA,6842
51
53
  general_manager/utils/testing.py,sha256=ElZ8p4iZHxsHjDN8Lm5TmI6527CW747ltDOmtY6gAhk,11872
52
- generalmanager-0.10.6.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
53
- generalmanager-0.10.6.dist-info/METADATA,sha256=SrGKXDLzzs69odSsBd3t7pE4DIuVS9lmlkyhbnAYgus,6206
54
- generalmanager-0.10.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
- generalmanager-0.10.6.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
56
- generalmanager-0.10.6.dist-info/RECORD,,
54
+ generalmanager-0.11.0.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
55
+ generalmanager-0.11.0.dist-info/METADATA,sha256=pMD3IcrqWIx1zEQIsMr2y1gS1nZjP8maVN29k8XSpqA,6206
56
+ generalmanager-0.11.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ generalmanager-0.11.0.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
58
+ generalmanager-0.11.0.dist-info/RECORD,,