utg-base 1.10.1__tar.gz → 1.11.1__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.
- {utg_base-1.10.1 → utg_base-1.11.1}/PKG-INFO +1 -1
- {utg_base-1.10.1 → utg_base-1.11.1}/pyproject.toml +1 -1
- utg_base-1.11.1/src/utg_base/permissions/decorators.py +25 -0
- utg_base-1.11.1/src/utg_base/permissions/utils.py +76 -0
- utg_base-1.11.1/src/utg_base/u_services/migrations/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/__init__.py +1 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/data.py +43 -0
- utg_base-1.11.1/src/utg_base/utils/string.py +41 -0
- utg_base-1.10.1/src/utg_base/utils/permission.py +0 -24
- {utg_base-1.10.1 → utg_base-1.11.1}/README.md +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/base.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/pagination.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/permissions.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/routers.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/serializers.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/spectacular.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/api/views.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/authentications/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/authentications/microservice_authentication.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/authentications/models.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/apps.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/filters/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/filters/task_result.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/management/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/management/commands/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/management/commands/migrate_tasks.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/serializers/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/serializers/periodic_task.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/serializers/task_result.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/urls.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/views/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/views/periodic_task.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/views/task_result.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/constants/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/constants/available_languages.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/env.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/logging.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/middleware/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/middleware/debug.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/middleware/locale.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/migration/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/migration/apps.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/migration/management/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/migration/management/commands/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/migration/management/commands/makemigrations.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/models/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/models/jwt_user.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/models/timestamp.py +0 -0
- {utg_base-1.10.1/src/utg_base/references_api → utg_base-1.11.1/src/utg_base/permissions}/__init__.py +0 -0
- {utg_base-1.10.1/src/utg_base/references_api/migrations → utg_base-1.11.1/src/utg_base/references_api}/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/references_api/admin.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/references_api/apps.py +0 -0
- {utg_base-1.10.1/src/utg_base/u_services → utg_base-1.11.1/src/utg_base/references_api/migrations}/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/references_api/models.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/references_api/urls.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/references_api/utils.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/services/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/services/base_api.py +0 -0
- {utg_base-1.10.1/src/utg_base/u_services/migrations → utg_base-1.11.1/src/utg_base/u_services}/__init__.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/u_services/apps.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/u_services/constants.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/u_services/migrations/0001_initial.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/u_services/models.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/u_services/u_requests.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/date.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/dict_util.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/response_processors.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/sql.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/thread.py +0 -0
- {utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/utils/translation.py +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from rest_framework.exceptions import PermissionDenied
|
|
5
|
+
|
|
6
|
+
from .utils import has_perms as has_permissions
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def has_perm(*permission: str, operator: Literal["OR", "AND"] = "OR"):
|
|
10
|
+
"""
|
|
11
|
+
Usage:
|
|
12
|
+
has_perm('PERM1')
|
|
13
|
+
has_perm('PERM1', 'PERM2')
|
|
14
|
+
|
|
15
|
+
:param permission: One or many permissions.
|
|
16
|
+
"""
|
|
17
|
+
def decorator(view_func):
|
|
18
|
+
view_func._perms = permission
|
|
19
|
+
@wraps(view_func)
|
|
20
|
+
def _wrapped_view(self, request, *args, **kwargs):
|
|
21
|
+
if not has_permissions(user_id=request.user.id, perms=list(permission), operator=operator):
|
|
22
|
+
raise PermissionDenied
|
|
23
|
+
return view_func(self, request, *args, **kwargs)
|
|
24
|
+
return _wrapped_view
|
|
25
|
+
return decorator
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from django.http import HttpRequest
|
|
4
|
+
from django.urls import URLResolver, URLPattern, get_resolver
|
|
5
|
+
from django_redis import get_redis_connection
|
|
6
|
+
from redis.client import Redis
|
|
7
|
+
from rest_framework.request import Request
|
|
8
|
+
|
|
9
|
+
from utg_base.env import env
|
|
10
|
+
from utg_base.u_services import u_requests
|
|
11
|
+
from utg_base.u_services.constants import UServices
|
|
12
|
+
from utg_base.utils import to_snake_case
|
|
13
|
+
from utg_base.utils.data import safe_get
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def generate_perm_cache_key(user_id: str):
|
|
17
|
+
return f"user:{user_id}:permissions"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def has_perm(user_id, perm):
|
|
21
|
+
redis_conn: Redis = get_redis_connection("shared")
|
|
22
|
+
|
|
23
|
+
return bool(redis_conn.sismember(f'user:{user_id}:permissions', perm))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def has_perms(user_id: str, perms: list[str], operator: Literal["OR", "AND"] = "OR"):
|
|
27
|
+
redis_conn: Redis = get_redis_connection("shared")
|
|
28
|
+
|
|
29
|
+
result = redis_conn.smismember(f'user:{user_id}:permissions', perms)
|
|
30
|
+
|
|
31
|
+
if operator == "OR":
|
|
32
|
+
return any(result)
|
|
33
|
+
return all(result)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_permissions(url_patterns):
|
|
37
|
+
permissions = set()
|
|
38
|
+
|
|
39
|
+
for item in url_patterns:
|
|
40
|
+
if isinstance(item, URLPattern):
|
|
41
|
+
view = item.callback
|
|
42
|
+
|
|
43
|
+
view_cls = safe_get(view, 'view_class')
|
|
44
|
+
# for View
|
|
45
|
+
if view_cls:
|
|
46
|
+
for method_name in ["get", "post", "put", "patch", "delete"]:
|
|
47
|
+
perms = safe_get(view_cls, f'{method_name}._perms', set())
|
|
48
|
+
permissions.update(perms)
|
|
49
|
+
# for ViewSet
|
|
50
|
+
else:
|
|
51
|
+
view_cls = safe_get(view, 'cls')
|
|
52
|
+
actions: dict = safe_get(view, 'actions', {})
|
|
53
|
+
for method_name in actions.values():
|
|
54
|
+
perms = safe_get(view_cls, f'{method_name}._perms', set())
|
|
55
|
+
permissions.update(perms)
|
|
56
|
+
|
|
57
|
+
elif isinstance(item, URLResolver):
|
|
58
|
+
permissions.update(_get_permissions(
|
|
59
|
+
item.url_patterns
|
|
60
|
+
))
|
|
61
|
+
|
|
62
|
+
return permissions
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def sync_permissions():
|
|
66
|
+
resolver = get_resolver()
|
|
67
|
+
perm_codes = list(_get_permissions(resolver.url_patterns))
|
|
68
|
+
|
|
69
|
+
request = Request(HttpRequest())
|
|
70
|
+
try:
|
|
71
|
+
u_requests.post(request, UServices.USER_MANAGEMENT, '/api/permissions/', data={
|
|
72
|
+
"service": to_snake_case(env('APP_NAME')),
|
|
73
|
+
"codes": perm_codes
|
|
74
|
+
})
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print(e)
|
|
File without changes
|
|
@@ -104,3 +104,46 @@ def safe_divide(*args, allow_null=False):
|
|
|
104
104
|
result /= value
|
|
105
105
|
|
|
106
106
|
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def safe_get(obj, dotted_path: str, default=None):
|
|
110
|
+
"""
|
|
111
|
+
Universal safe getter: supports dicts, objects, lists, and mixes of them.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
obj: Any object, dict, list, etc.
|
|
115
|
+
dotted_path (str): Path like 'a.b.c' or 'items.0.name'.
|
|
116
|
+
default: Returned if path does not exist.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Any: The resolved value or default.
|
|
120
|
+
"""
|
|
121
|
+
parts = dotted_path.split('.')
|
|
122
|
+
|
|
123
|
+
for part in parts:
|
|
124
|
+
if obj is None:
|
|
125
|
+
return default
|
|
126
|
+
|
|
127
|
+
# --- Handle LIST index ---
|
|
128
|
+
if isinstance(obj, (list, tuple)) and part.isdigit():
|
|
129
|
+
idx = int(part)
|
|
130
|
+
if 0 <= idx < len(obj):
|
|
131
|
+
obj = obj[idx]
|
|
132
|
+
else:
|
|
133
|
+
return default
|
|
134
|
+
|
|
135
|
+
# --- Handle DICT ---
|
|
136
|
+
elif isinstance(obj, dict):
|
|
137
|
+
if part in obj:
|
|
138
|
+
obj = obj.get(part)
|
|
139
|
+
else:
|
|
140
|
+
return default
|
|
141
|
+
|
|
142
|
+
# --- Handle OBJECT attribute ---
|
|
143
|
+
else:
|
|
144
|
+
if hasattr(obj, part):
|
|
145
|
+
obj = getattr(obj, part)
|
|
146
|
+
else:
|
|
147
|
+
return default
|
|
148
|
+
|
|
149
|
+
return obj
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
def to_snake_case(s: str) -> str:
|
|
4
|
+
"""
|
|
5
|
+
Convert any string into snake_case.
|
|
6
|
+
Handles:
|
|
7
|
+
- camelCase / PascalCase
|
|
8
|
+
- mixed separators (-, space, ., !, etc.)
|
|
9
|
+
- multiple underscores
|
|
10
|
+
- trims leading/trailing separators
|
|
11
|
+
"""
|
|
12
|
+
if not isinstance(s, str):
|
|
13
|
+
raise TypeError("Input must be a string")
|
|
14
|
+
|
|
15
|
+
# Trim spaces
|
|
16
|
+
s = s.strip()
|
|
17
|
+
if not s:
|
|
18
|
+
return ""
|
|
19
|
+
|
|
20
|
+
# Insert underscore between lower/number and upper case letters
|
|
21
|
+
# "helloWorld" -> "hello_World"
|
|
22
|
+
s = re.sub(r'(?<=[0-9a-z])(?=[A-Z])', '_', s)
|
|
23
|
+
|
|
24
|
+
# Insert underscore between acronyms and normal words
|
|
25
|
+
# "HTTPResponse" -> "HTTP_Response"
|
|
26
|
+
s = re.sub(r'(?<=[A-Z])(?=[A-Z][a-z])', '_', s)
|
|
27
|
+
|
|
28
|
+
# Replace all non-word chars with underscore
|
|
29
|
+
# \W = anything except letters, digits, underscore
|
|
30
|
+
s = re.sub(r'\W+', '_', s)
|
|
31
|
+
|
|
32
|
+
# Replace spaces if any remain
|
|
33
|
+
s = s.replace(" ", "_")
|
|
34
|
+
|
|
35
|
+
# Collapse multiple underscores
|
|
36
|
+
s = re.sub(r'_+', '_', s)
|
|
37
|
+
|
|
38
|
+
# Remove leading/trailing underscores
|
|
39
|
+
s = s.strip('_')
|
|
40
|
+
|
|
41
|
+
return s.lower()
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from typing import Literal
|
|
2
|
-
|
|
3
|
-
from django_redis import get_redis_connection
|
|
4
|
-
from redis.client import Redis
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def generate_perm_cache_key(user_id: str):
|
|
8
|
-
return f"user:{user_id}:permissions"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def has_perm(user_id, perm):
|
|
12
|
-
redis_conn: Redis = get_redis_connection("shared")
|
|
13
|
-
|
|
14
|
-
return bool(redis_conn.sismember(f'user:{user_id}:permissions', perm))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def has_perms(user_id: str, perms: list[str], operator: Literal["OR", "AND"] = "OR"):
|
|
18
|
-
redis_conn: Redis = get_redis_connection("shared")
|
|
19
|
-
|
|
20
|
-
result = redis_conn.smismember(f'user:{user_id}:permissions', perms)
|
|
21
|
-
|
|
22
|
-
if operator == "OR":
|
|
23
|
-
return any(result)
|
|
24
|
-
return all(result)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/authentications/microservice_authentication.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/celery/management/commands/migrate_tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{utg_base-1.10.1 → utg_base-1.11.1}/src/utg_base/migration/management/commands/makemigrations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{utg_base-1.10.1/src/utg_base/references_api → utg_base-1.11.1/src/utg_base/permissions}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|