GeneralManager 0.10.6__tar.gz → 0.11.0__tar.gz

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.
Files changed (65) hide show
  1. {generalmanager-0.10.6 → generalmanager-0.11.0}/GeneralManager.egg-info/PKG-INFO +1 -1
  2. {generalmanager-0.10.6 → generalmanager-0.11.0}/GeneralManager.egg-info/SOURCES.txt +2 -0
  3. {generalmanager-0.10.6 → generalmanager-0.11.0}/PKG-INFO +1 -1
  4. {generalmanager-0.10.6 → generalmanager-0.11.0}/pyproject.toml +1 -1
  5. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/api/mutation.py +15 -7
  6. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/permission/basePermission.py +7 -21
  7. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/permission/managerBasedPermission.py +2 -11
  8. generalmanager-0.11.0/src/general_manager/permission/mutationPermission.py +88 -0
  9. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/permission/permissionDataManager.py +1 -5
  10. generalmanager-0.11.0/src/general_manager/permission/utils.py +35 -0
  11. {generalmanager-0.10.6 → generalmanager-0.11.0}/GeneralManager.egg-info/dependency_links.txt +0 -0
  12. {generalmanager-0.10.6 → generalmanager-0.11.0}/GeneralManager.egg-info/requires.txt +0 -0
  13. {generalmanager-0.10.6 → generalmanager-0.11.0}/GeneralManager.egg-info/top_level.txt +0 -0
  14. {generalmanager-0.10.6 → generalmanager-0.11.0}/LICENSE +0 -0
  15. {generalmanager-0.10.6 → generalmanager-0.11.0}/README.md +0 -0
  16. {generalmanager-0.10.6 → generalmanager-0.11.0}/setup.cfg +0 -0
  17. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/__init__.py +0 -0
  18. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/api/graphql.py +0 -0
  19. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/api/property.py +0 -0
  20. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/apps.py +0 -0
  21. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/bucket/baseBucket.py +0 -0
  22. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/bucket/calculationBucket.py +0 -0
  23. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/bucket/databaseBucket.py +0 -0
  24. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/bucket/groupBucket.py +0 -0
  25. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/cache/cacheDecorator.py +0 -0
  26. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/cache/cacheTracker.py +0 -0
  27. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/cache/dependencyIndex.py +0 -0
  28. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/cache/modelDependencyCollector.py +0 -0
  29. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/cache/signals.py +0 -0
  30. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/factory/__init__.py +0 -0
  31. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/factory/autoFactory.py +0 -0
  32. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/factory/factories.py +0 -0
  33. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/factory/factoryMethods.py +0 -0
  34. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/interface/__init__.py +0 -0
  35. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/interface/baseInterface.py +0 -0
  36. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/interface/calculationInterface.py +0 -0
  37. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/interface/databaseBasedInterface.py +0 -0
  38. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/interface/databaseInterface.py +0 -0
  39. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/interface/models.py +0 -0
  40. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/interface/readOnlyInterface.py +0 -0
  41. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/manager/__init__.py +0 -0
  42. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/manager/generalManager.py +0 -0
  43. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/manager/groupManager.py +0 -0
  44. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/manager/input.py +0 -0
  45. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/manager/meta.py +0 -0
  46. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/measurement/__init__.py +0 -0
  47. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/measurement/measurement.py +0 -0
  48. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/measurement/measurementField.py +0 -0
  49. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/permission/__init__.py +0 -0
  50. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/permission/fileBasedPermission.py +0 -0
  51. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/permission/permissionChecks.py +0 -0
  52. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/rule/__init__.py +0 -0
  53. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/rule/handler.py +0 -0
  54. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/rule/rule.py +0 -0
  55. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/__init__.py +0 -0
  56. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/argsToKwargs.py +0 -0
  57. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/filterParser.py +0 -0
  58. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/formatString.py +0 -0
  59. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/jsonEncoder.py +0 -0
  60. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/makeCacheKey.py +0 -0
  61. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/noneToZero.py +0 -0
  62. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/pathMapping.py +0 -0
  63. {generalmanager-0.10.6 → generalmanager-0.11.0}/src/general_manager/utils/testing.py +0 -0
  64. {generalmanager-0.10.6 → generalmanager-0.11.0}/tests/test_settings.py +0 -0
  65. {generalmanager-0.10.6 → generalmanager-0.11.0}/tests/test_urls.py +0 -0
@@ -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
@@ -43,8 +43,10 @@ src/general_manager/permission/__init__.py
43
43
  src/general_manager/permission/basePermission.py
44
44
  src/general_manager/permission/fileBasedPermission.py
45
45
  src/general_manager/permission/managerBasedPermission.py
46
+ src/general_manager/permission/mutationPermission.py
46
47
  src/general_manager/permission/permissionChecks.py
47
48
  src/general_manager/permission/permissionDataManager.py
49
+ src/general_manager/permission/utils.py
48
50
  src/general_manager/rule/__init__.py
49
51
  src/general_manager/rule/handler.py
50
52
  src/general_manager/rule/rule.py
@@ -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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.10.6"
7
+ version = "0.11.0"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -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
+ )
File without changes