drf-iam 0.2.7__py3-none-any.whl → 0.2.8__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.
- drf_iam/utils/load_viewset_permissions.py +25 -20
- {drf_iam-0.2.7.dist-info → drf_iam-0.2.8.dist-info}/METADATA +1 -1
- {drf_iam-0.2.7.dist-info → drf_iam-0.2.8.dist-info}/RECORD +5 -5
- {drf_iam-0.2.7.dist-info → drf_iam-0.2.8.dist-info}/WHEEL +0 -0
- {drf_iam-0.2.7.dist-info → drf_iam-0.2.8.dist-info}/top_level.txt +0 -0
@@ -5,8 +5,8 @@ from typing import List, Dict, Any, Set, Tuple, Iterator, Type, Optional
|
|
5
5
|
|
6
6
|
from django.conf import settings
|
7
7
|
from django.urls import get_resolver, URLPattern, URLResolver
|
8
|
-
from rest_framework.viewsets import ViewSetMixin
|
9
8
|
from rest_framework.views import APIView
|
9
|
+
from rest_framework.viewsets import ViewSetMixin
|
10
10
|
|
11
11
|
from drf_iam.models import Policy
|
12
12
|
from .logging_utils import setup_colorful_logger
|
@@ -36,9 +36,10 @@ def is_viewset(cls: Any) -> bool:
|
|
36
36
|
"""Checks if the given class is a DRF ViewSet."""
|
37
37
|
return isinstance(cls, type) and (issubclass(cls, ViewSetMixin))
|
38
38
|
|
39
|
+
|
39
40
|
def is_api_view(cls: Any) -> bool:
|
40
41
|
"""Checks if the given class is a DRF APIView."""
|
41
|
-
return isinstance(cls, type) and
|
42
|
+
return isinstance(cls, type) and issubclass(cls, APIView)
|
42
43
|
|
43
44
|
|
44
45
|
def get_viewset_actions(viewset_cls: Type[ViewSetMixin]) -> Set[str]:
|
@@ -47,7 +48,7 @@ def get_viewset_actions(viewset_cls: Type[ViewSetMixin]) -> Set[str]:
|
|
47
48
|
Checks Django settings for 'DRF_IAM_DEFAULT_VIEWSET_ACTIONS'.
|
48
49
|
"""
|
49
50
|
actions: Set[str] = set()
|
50
|
-
|
51
|
+
|
51
52
|
fallback_default_actions = {
|
52
53
|
'list', 'retrieve', 'create', 'update', 'partial_update', 'destroy'
|
53
54
|
}
|
@@ -94,10 +95,10 @@ class PermissionLoader:
|
|
94
95
|
}
|
95
96
|
|
96
97
|
def _validate_conflicting_definitions(
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
98
|
+
self,
|
99
|
+
action_method: Any,
|
100
|
+
action_name: str,
|
101
|
+
iam_perms_for_action: Dict[str, Any]
|
101
102
|
) -> None:
|
102
103
|
"""
|
103
104
|
Validates that IAM-related attributes are not defined in conflicting ways
|
@@ -113,7 +114,8 @@ class PermissionLoader:
|
|
113
114
|
"Please choose a single source of truth."
|
114
115
|
)
|
115
116
|
|
116
|
-
viewset_name = action_method.__self__.__class__.__name__ if hasattr(action_method,
|
117
|
+
viewset_name = action_method.__self__.__class__.__name__ if hasattr(action_method,
|
118
|
+
'__self__') else 'UnknownViewSet'
|
117
119
|
|
118
120
|
# Check for 'policy_name'
|
119
121
|
if iam_perms_for_action.get("policy_name") and hasattr(action_method, 'policy_name'):
|
@@ -121,11 +123,12 @@ class PermissionLoader:
|
|
121
123
|
|
122
124
|
# Check for 'policy_description' (key 'description' in dict, attribute 'policy_description' on method)
|
123
125
|
if iam_perms_for_action.get("description") and hasattr(action_method, 'policy_description'):
|
124
|
-
conflicts.append(
|
126
|
+
conflicts.append(
|
127
|
+
error_message_template.format('policy_description (dict key "description")', action_name, viewset_name))
|
125
128
|
|
126
129
|
# Check for 'exclude_from_iam'
|
127
130
|
if iam_perms_for_action.get("exclude_from_iam") is not None and \
|
128
|
-
|
131
|
+
hasattr(action_method, 'exclude_from_iam'):
|
129
132
|
conflicts.append(error_message_template.format('exclude_from_iam', action_name, viewset_name))
|
130
133
|
|
131
134
|
if conflicts:
|
@@ -139,15 +142,16 @@ class PermissionLoader:
|
|
139
142
|
for viewset_info in self._extract_viewsets_from_urlpatterns(self.urlpatterns):
|
140
143
|
viewset_cls = viewset_info['callback'].cls
|
141
144
|
actions = get_viewset_actions(viewset_cls)
|
142
|
-
resource_name_from_path = viewset_info['name'] # Used if iam_resource_type_name not set
|
143
145
|
|
144
146
|
# Cache resource type name
|
145
147
|
if viewset_cls not in resource_type_name_cache:
|
146
148
|
resource_type_name_cache[viewset_cls] = getattr(
|
147
149
|
viewset_cls,
|
148
|
-
"
|
149
|
-
|
150
|
-
)
|
150
|
+
"iam_policy_name",
|
151
|
+
None
|
152
|
+
) or viewset_cls.__name__.lower().replace('viewset',
|
153
|
+
'')
|
154
|
+
|
151
155
|
resource_type_name = resource_type_name_cache[viewset_cls]
|
152
156
|
|
153
157
|
drf_iam_permissions = getattr(viewset_cls, "drf_iam_permissions", {})
|
@@ -156,7 +160,7 @@ class PermissionLoader:
|
|
156
160
|
action_method = getattr(viewset_cls, action_name, None)
|
157
161
|
iam_perms_for_action = drf_iam_permissions.get(action_name, {})
|
158
162
|
|
159
|
-
if action_method:
|
163
|
+
if action_method: # Ensure the method actually exists
|
160
164
|
self._validate_conflicting_definitions(
|
161
165
|
action_method,
|
162
166
|
action_name,
|
@@ -170,7 +174,7 @@ class PermissionLoader:
|
|
170
174
|
excluded = getattr(action_method, 'exclude_from_iam')
|
171
175
|
elif "exclude_from_iam" in iam_perms_for_action:
|
172
176
|
excluded = iam_perms_for_action.get("exclude_from_iam", False)
|
173
|
-
|
177
|
+
|
174
178
|
if excluded:
|
175
179
|
logger.debug(
|
176
180
|
f"Action '{action_name}' on ViewSet "
|
@@ -197,14 +201,14 @@ class PermissionLoader:
|
|
197
201
|
description_str = iam_perms_for_action["description"]
|
198
202
|
else:
|
199
203
|
description_str = f"Allows to {action_name.replace('_', ' ')} for {resource_type_name.replace('_', ' ')}"
|
200
|
-
|
204
|
+
|
201
205
|
raw_policy_details.append({
|
202
|
-
"action": action_name,
|
206
|
+
"action": action_name, # Just the action name
|
203
207
|
"resource_type": resource_type_name,
|
204
208
|
"policy_name": policy_name_str,
|
205
209
|
"description": description_str,
|
206
210
|
})
|
207
|
-
|
211
|
+
|
208
212
|
# Remove duplicates and create PolicyDetail objects
|
209
213
|
# Ensures that if multiple URL patterns point to the same viewset action,
|
210
214
|
# we only consider one policy detail for it based on action and resource_type.
|
@@ -245,7 +249,8 @@ class PermissionLoader:
|
|
245
249
|
to_create = list(desired_set - current_set)
|
246
250
|
|
247
251
|
policies_to_delete_with_id: List[PolicyDetail] = []
|
248
|
-
current_map_for_delete = {(p.action, p.resource_type): p.id for p in self.current_db_policies if
|
252
|
+
current_map_for_delete = {(p.action, p.resource_type): p.id for p in self.current_db_policies if
|
253
|
+
p.id is not None}
|
249
254
|
policies_to_delete_stubs = current_set - desired_set
|
250
255
|
for policy_stub in policies_to_delete_stubs:
|
251
256
|
policy_id = current_map_for_delete.get((policy_stub.action, policy_stub.resource_type))
|
@@ -9,9 +9,9 @@ drf_iam/migrations/0001_initial.py,sha256=y_4jXnr7gjU4UXxVrgVrStTSFu3h1ZrmjEZDL4
|
|
9
9
|
drf_iam/migrations/0002_add_policy_name.py,sha256=EUZ2OCmobOlmnlpYv0jsMBb3QR0HknW4qAjgn-zOzA0,353
|
10
10
|
drf_iam/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
drf_iam/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
drf_iam/utils/load_viewset_permissions.py,sha256=
|
12
|
+
drf_iam/utils/load_viewset_permissions.py,sha256=HtEQqZ48NOtYtpyDNLg0eMJFkdImkEaq8YEMADD0I_c,15099
|
13
13
|
drf_iam/utils/logging_utils.py,sha256=9I9hhSVgOVW5xdKXSKofbjgl6rMYbmAI40euiB5WNlM,2074
|
14
|
-
drf_iam-0.2.
|
15
|
-
drf_iam-0.2.
|
16
|
-
drf_iam-0.2.
|
17
|
-
drf_iam-0.2.
|
14
|
+
drf_iam-0.2.8.dist-info/METADATA,sha256=EUbV0JxfcIP6MI_CXjhbes7tR8euXBmzV8rapZgIWBA,2763
|
15
|
+
drf_iam-0.2.8.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
16
|
+
drf_iam-0.2.8.dist-info/top_level.txt,sha256=daz6AaQ9e_cfCjLk2aRoLb_PCOoFofYUX4DU85VwHSM,8
|
17
|
+
drf_iam-0.2.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|