oxutils 0.1.12__py3-none-any.whl → 0.1.15__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.
- oxutils/__init__.py +1 -1
- oxutils/jwt/auth.py +39 -1
- oxutils/jwt/middleware.py +404 -0
- oxutils/jwt/models.py +98 -2
- oxutils/jwt/tokens.py +8 -0
- oxutils/oxiliere/middleware.py +46 -5
- oxutils/oxiliere/permissions.py +68 -47
- oxutils/oxiliere/schemas.py +31 -0
- oxutils/permissions/caches.py +17 -3
- oxutils/permissions/perms.py +106 -0
- oxutils/permissions/utils.py +178 -22
- oxutils/users/migrations/0003_user_photo.py +18 -0
- oxutils/users/models.py +1 -0
- {oxutils-0.1.12.dist-info → oxutils-0.1.15.dist-info}/METADATA +1 -1
- {oxutils-0.1.12.dist-info → oxutils-0.1.15.dist-info}/RECORD +16 -14
- {oxutils-0.1.12.dist-info → oxutils-0.1.15.dist-info}/WHEEL +0 -0
oxutils/oxiliere/middleware.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
2
3
|
from django.db import connection
|
|
3
4
|
from django.http import Http404
|
|
4
5
|
from django.urls import set_urlconf
|
|
5
6
|
from django.utils.module_loading import import_string
|
|
6
7
|
from django.utils.deprecation import MiddlewareMixin
|
|
7
8
|
|
|
9
|
+
import structlog
|
|
10
|
+
|
|
8
11
|
from django_tenants.utils import (
|
|
9
12
|
get_public_schema_name,
|
|
10
13
|
get_public_schema_urlconf,
|
|
@@ -23,6 +26,9 @@ from oxutils.oxiliere.context import set_current_tenant_schema_name
|
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
|
|
29
|
+
logger = structlog.get_logger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
26
32
|
|
|
27
33
|
class TenantMainMiddleware(MiddlewareMixin):
|
|
28
34
|
TENANT_NOT_FOUND_EXCEPTION = Http404
|
|
@@ -44,6 +50,22 @@ class TenantMainMiddleware(MiddlewareMixin):
|
|
|
44
50
|
"""
|
|
45
51
|
return tenant_model.objects.get(oxi_id=oxi_id)
|
|
46
52
|
|
|
53
|
+
def get_tenant_user(self, tenant, user, raise_exception=False):
|
|
54
|
+
""" Get tenant user by tenant and user.
|
|
55
|
+
"""
|
|
56
|
+
if not tenant or not user:
|
|
57
|
+
if raise_exception:
|
|
58
|
+
raise ObjectDoesNotExist("tenant_user_not_found, tenant or user is None")
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
return tenant.users.select_related('user').get(user__pk=user.id)
|
|
63
|
+
except ObjectDoesNotExist:
|
|
64
|
+
logger.error("tenant_user_not_found", tenant_id=tenant.id, user_id=user.id)
|
|
65
|
+
if raise_exception:
|
|
66
|
+
raise ObjectDoesNotExist("tenant_user_not_found")
|
|
67
|
+
return None
|
|
68
|
+
|
|
47
69
|
def process_request(self, request):
|
|
48
70
|
# Connection needs first to be at the public schema, as this is where
|
|
49
71
|
# the tenant metadata is stored.
|
|
@@ -51,42 +73,61 @@ class TenantMainMiddleware(MiddlewareMixin):
|
|
|
51
73
|
connection.set_schema_to_public()
|
|
52
74
|
|
|
53
75
|
oxi_id = self.get_org_id_from_request(request)
|
|
76
|
+
tenant_model = connection.tenant_model
|
|
54
77
|
|
|
55
78
|
# Try to get tenant from cookie token first
|
|
56
79
|
tenant_token = request.COOKIES.get(ORGANIZATION_TOKEN_COOKIE_KEY)
|
|
57
80
|
tenant = None
|
|
81
|
+
old_tenant = None
|
|
58
82
|
request._should_set_tenant_cookie = False
|
|
59
83
|
|
|
60
84
|
if tenant_token:
|
|
61
85
|
tenant = TokenTenant.for_token(tenant_token)
|
|
62
86
|
# Verify the token's oxi_id matches the request
|
|
63
87
|
if not is_system_tenant(tenant) and tenant.oxi_id != oxi_id:
|
|
88
|
+
logger.info("tenant_token_oxi_id_doesnt_match_request_oxi_id", tenant_oxi_id=tenant.oxi_id, request_oxi_id=oxi_id)
|
|
89
|
+
old_tenant = tenant
|
|
64
90
|
tenant = None
|
|
65
91
|
|
|
66
92
|
# If no valid token, fetch from database
|
|
67
93
|
if not tenant:
|
|
68
94
|
if oxi_id: # fetch with oxi_id on tenant
|
|
69
|
-
tenant_model = connection.tenant_model
|
|
70
95
|
try:
|
|
71
96
|
tenant = self.get_tenant(tenant_model, oxi_id)
|
|
97
|
+
tenant.user = self.get_tenant_user(tenant, request.user, raise_exception=True)
|
|
98
|
+
|
|
72
99
|
# Mark that we need to set the cookie in the response
|
|
73
100
|
request._should_set_tenant_cookie = True
|
|
74
|
-
|
|
101
|
+
|
|
102
|
+
if old_tenant:
|
|
103
|
+
logger.info("tenant_changed", old_tenant=old_tenant.oxi_id, new_tenant=tenant.oxi_id)
|
|
104
|
+
|
|
105
|
+
except ObjectDoesNotExist as ex:
|
|
106
|
+
logger.error("tenant_not_found", oxi_id=oxi_id, error=str(ex))
|
|
75
107
|
default_tenant = self.no_tenant_found(request, oxi_id)
|
|
76
108
|
return default_tenant
|
|
77
109
|
else: # try to return the system tenant
|
|
78
110
|
try:
|
|
79
111
|
from oxutils.oxiliere.caches import get_system_tenant
|
|
80
112
|
tenant = get_system_tenant()
|
|
113
|
+
tenant.user = self.get_tenant_user(tenant, request.user, raise_exception=False)
|
|
81
114
|
request._should_set_tenant_cookie = True
|
|
82
115
|
except Exception as e:
|
|
116
|
+
logger.error("system_tenant_not_found", error=str(e))
|
|
83
117
|
from django.http import HttpResponseBadRequest
|
|
84
118
|
return HttpResponseBadRequest('Missing X-Organization-ID header')
|
|
85
119
|
|
|
86
120
|
if tenant.is_deleted or not tenant.is_active:
|
|
121
|
+
logger.error("tenant_is_deleted_or_inactive", oxi_id=oxi_id)
|
|
87
122
|
return self.no_tenant_found(request, oxi_id)
|
|
88
123
|
|
|
89
|
-
|
|
124
|
+
if tenant and not isinstance(tenant, TokenTenant):
|
|
125
|
+
request.db_tenant = tenant
|
|
126
|
+
else:
|
|
127
|
+
request.db_tenant = None
|
|
128
|
+
|
|
129
|
+
request.tenant = TokenTenant.from_db(tenant)
|
|
130
|
+
|
|
90
131
|
set_current_tenant_schema_name(tenant.schema_name)
|
|
91
132
|
connection.set_tenant(request.tenant)
|
|
92
133
|
self.setup_url_routing(request)
|
|
@@ -94,9 +135,9 @@ class TenantMainMiddleware(MiddlewareMixin):
|
|
|
94
135
|
def process_response(self, request, response):
|
|
95
136
|
"""Set the tenant token cookie if needed."""
|
|
96
137
|
if hasattr(request, '_should_set_tenant_cookie') and request._should_set_tenant_cookie:
|
|
97
|
-
if hasattr(request, '
|
|
138
|
+
if hasattr(request, 'db_tenant') and isinstance(request.db_tenant, connection.tenant_model):
|
|
98
139
|
# Generate token from DB tenant
|
|
99
|
-
token = OrganizationAccessToken.for_tenant(request.
|
|
140
|
+
token = OrganizationAccessToken.for_tenant(request.db_tenant)
|
|
100
141
|
response.set_cookie(
|
|
101
142
|
key=ORGANIZATION_TOKEN_COOKIE_KEY,
|
|
102
143
|
value=str(token),
|
oxutils/oxiliere/permissions.py
CHANGED
|
@@ -1,81 +1,95 @@
|
|
|
1
|
+
import structlog
|
|
2
|
+
|
|
1
3
|
from ninja_extra.permissions import BasePermission
|
|
2
|
-
from oxutils.oxiliere.utils import get_tenant_user_model
|
|
3
4
|
from oxutils.constants import OXILIERE_SERVICE_TOKEN
|
|
4
5
|
from oxutils.jwt.tokens import OxilierServiceToken
|
|
6
|
+
from oxutils.jwt.models import TokenTenant
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
|
|
10
|
+
logger = structlog.get_logger(__name__)
|
|
6
11
|
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TenantBasePermission(BasePermission):
|
|
9
16
|
"""
|
|
10
17
|
Vérifie que l'utilisateur a accès au tenant actuel.
|
|
11
18
|
L'utilisateur doit être authentifié et avoir un lien avec le tenant.
|
|
12
19
|
"""
|
|
20
|
+
def check_tenant_permission(self, request) -> bool:
|
|
21
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
22
|
+
|
|
13
23
|
def has_permission(self, request, **kwargs):
|
|
14
24
|
if not request.user or not request.user.is_authenticated:
|
|
15
25
|
return False
|
|
16
26
|
|
|
17
27
|
if not hasattr(request, 'tenant'):
|
|
28
|
+
logger.warning('tenant_permission', type="tenant_not_found", user=request.user)
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
if not isinstance(request.tenant, TokenTenant):
|
|
32
|
+
logger.warning(
|
|
33
|
+
'tenant_permission',
|
|
34
|
+
type="tenant_is_not_token_tenant",
|
|
35
|
+
tenant=request.tenant,
|
|
36
|
+
user=request.user
|
|
37
|
+
)
|
|
18
38
|
return False
|
|
19
|
-
|
|
20
|
-
# Vérifier que l'utilisateur a accès à ce tenant
|
|
21
|
-
return get_tenant_user_model().objects.filter(
|
|
22
|
-
tenant__pk=request.tenant.pk,
|
|
23
|
-
user__pk=request.user.pk
|
|
24
|
-
).exists()
|
|
25
39
|
|
|
40
|
+
return self.check_tenant_permission(request)
|
|
26
41
|
|
|
27
|
-
|
|
42
|
+
|
|
43
|
+
class TenantUserPermission(TenantBasePermission):
|
|
28
44
|
"""
|
|
29
|
-
Vérifie que l'utilisateur est
|
|
45
|
+
Vérifie que l'utilisateur est un membre du tenant actuel.
|
|
46
|
+
Alias de TenantPermission pour plus de clarté sémantique.
|
|
30
47
|
"""
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
def check_tenant_permission(self, request) -> bool:
|
|
49
|
+
tenant: TokenTenant = request.tenant
|
|
50
|
+
|
|
51
|
+
logger.info(
|
|
52
|
+
'tenant_permission',
|
|
53
|
+
type="tenant_user_access_permission",
|
|
54
|
+
tenant=tenant, user=request.user,
|
|
55
|
+
passed=tenant.is_tenant_user
|
|
56
|
+
)
|
|
37
57
|
|
|
38
|
-
return
|
|
39
|
-
tenant__pk=request.tenant.pk,
|
|
40
|
-
user__pk=request.user.pk,
|
|
41
|
-
is_owner=True
|
|
42
|
-
).exists()
|
|
58
|
+
return tenant.is_tenant_user
|
|
43
59
|
|
|
44
60
|
|
|
45
|
-
class
|
|
61
|
+
class TenantOwnerPermission(TenantBasePermission):
|
|
46
62
|
"""
|
|
47
|
-
Vérifie que l'utilisateur est
|
|
63
|
+
Vérifie que l'utilisateur est propriétaire (owner) du tenant actuel.
|
|
48
64
|
"""
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
def check_tenant_permission(self, request) -> bool:
|
|
66
|
+
tenant: TokenTenant = request.tenant
|
|
67
|
+
|
|
68
|
+
logger.info(
|
|
69
|
+
'tenant_permission',
|
|
70
|
+
type="tenant_user_access_permission",
|
|
71
|
+
tenant=tenant, user=request.user,
|
|
72
|
+
passed=tenant.is_owner_user
|
|
73
|
+
)
|
|
55
74
|
|
|
56
|
-
return
|
|
57
|
-
tenant__pk=request.tenant.pk,
|
|
58
|
-
user__pk=request.user.pk,
|
|
59
|
-
is_admin=True
|
|
60
|
-
).exists()
|
|
75
|
+
return tenant.is_owner_user
|
|
61
76
|
|
|
62
77
|
|
|
63
|
-
class
|
|
78
|
+
class TenantAdminPermission(TenantBasePermission):
|
|
64
79
|
"""
|
|
65
|
-
Vérifie que l'utilisateur est
|
|
66
|
-
Alias de TenantPermission pour plus de clarté sémantique.
|
|
80
|
+
Vérifie que l'utilisateur est admin ou owner du tenant actuel.
|
|
67
81
|
"""
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
def check_tenant_permission(self, request) -> bool:
|
|
83
|
+
tenant: TokenTenant = request.tenant
|
|
84
|
+
|
|
85
|
+
logger.info(
|
|
86
|
+
'tenant_permission',
|
|
87
|
+
type="tenant_user_access_permission",
|
|
88
|
+
tenant=tenant, user=request.user,
|
|
89
|
+
passed=tenant.is_admin_user
|
|
90
|
+
)
|
|
74
91
|
|
|
75
|
-
return
|
|
76
|
-
tenant__pk=request.tenant.pk,
|
|
77
|
-
user__pk=request.user.pk
|
|
78
|
-
).exists()
|
|
92
|
+
return tenant.is_admin_user
|
|
79
93
|
|
|
80
94
|
|
|
81
95
|
class OxiliereServicePermission(BasePermission):
|
|
@@ -95,3 +109,10 @@ class OxiliereServicePermission(BasePermission):
|
|
|
95
109
|
return True
|
|
96
110
|
except Exception:
|
|
97
111
|
return False
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
IsTenantUser = TenantUserPermission()
|
|
116
|
+
IsTenantOwner = TenantOwnerPermission()
|
|
117
|
+
IsTenantAdmin = TenantAdminPermission()
|
|
118
|
+
IsOxiliereService = OxiliereServicePermission()
|
oxutils/oxiliere/schemas.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
from uuid import UUID
|
|
3
3
|
from ninja import Schema
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
6
|
+
from django.utils.module_loading import import_string
|
|
4
7
|
from django.db import transaction
|
|
5
8
|
from django.contrib.auth import get_user_model
|
|
6
9
|
from django_tenants.utils import get_tenant_model
|
|
@@ -13,6 +16,18 @@ import structlog
|
|
|
13
16
|
logger = structlog.get_logger(__name__)
|
|
14
17
|
|
|
15
18
|
|
|
19
|
+
|
|
20
|
+
def get_tenant_schema() -> 'TenantSchema':
|
|
21
|
+
if hasattr(settings, 'OX_TENANT_SCHEMA'):
|
|
22
|
+
try:
|
|
23
|
+
return import_string(settings.OX_TENANT_SCHEMA)
|
|
24
|
+
except ImportError as e:
|
|
25
|
+
raise ImproperlyConfigured(
|
|
26
|
+
f"Error: OX_TENANT_SCHEMA import error: {settings.OX_TENANT_SCHEMA}, please check your settings"
|
|
27
|
+
) from e
|
|
28
|
+
return TenantSchema
|
|
29
|
+
|
|
30
|
+
|
|
16
31
|
class TenantSchema(Schema):
|
|
17
32
|
name: str
|
|
18
33
|
oxi_id: str
|
|
@@ -29,6 +44,22 @@ class TenantOwnerSchema(Schema):
|
|
|
29
44
|
email: str
|
|
30
45
|
|
|
31
46
|
|
|
47
|
+
class UserSchema(Schema):
|
|
48
|
+
oxi_id: UUID
|
|
49
|
+
first_name: Optional[str] = None
|
|
50
|
+
last_name: Optional[str] = None
|
|
51
|
+
email: str
|
|
52
|
+
is_active: bool
|
|
53
|
+
photo: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TenantUser(Schema):
|
|
57
|
+
user: UserSchema
|
|
58
|
+
is_owner: bool
|
|
59
|
+
is_admin: bool
|
|
60
|
+
status: str
|
|
61
|
+
|
|
62
|
+
|
|
32
63
|
class CreateTenantSchema(Schema):
|
|
33
64
|
tenant: TenantSchema
|
|
34
65
|
owner: TenantOwnerSchema
|
oxutils/permissions/caches.py
CHANGED
|
@@ -7,13 +7,27 @@ CACHE_CHECK_PERMISSION = getattr(settings, 'CACHE_CHECK_PERMISSION', False)
|
|
|
7
7
|
if CACHE_CHECK_PERMISSION:
|
|
8
8
|
from cacheops import cached_as
|
|
9
9
|
from .models import Grant
|
|
10
|
-
from .utils import check
|
|
10
|
+
from .utils import check, any_action_check, any_permission_check
|
|
11
11
|
|
|
12
|
-
@cached_as(Grant, timeout=60*
|
|
12
|
+
@cached_as(Grant, timeout=60*15)
|
|
13
13
|
def cache_check(user, scope, actions, group = None, **context):
|
|
14
14
|
return check(user, scope, actions, group, **context)
|
|
15
|
+
|
|
16
|
+
@cached_as(Grant, timeout=60*15)
|
|
17
|
+
def cache_any_action_check(user, scope, required, group = None, **context):
|
|
18
|
+
return any_action_check(user, scope, required, group, **context)
|
|
19
|
+
|
|
20
|
+
@cached_as(Grant, timeout=60*15)
|
|
21
|
+
def cache_any_permission_check(user, *str_perms):
|
|
22
|
+
return any_permission_check(user, *str_perms)
|
|
15
23
|
else:
|
|
16
|
-
from .utils import check
|
|
24
|
+
from .utils import check, any_action_check, any_permission_check
|
|
17
25
|
|
|
18
26
|
def cache_check(user, scope, actions, group = None, **context):
|
|
19
27
|
return check(user, scope, actions, group, **context)
|
|
28
|
+
|
|
29
|
+
def cache_any_action_check(user, scope, required, group = None, **context):
|
|
30
|
+
return any_action_check(user, scope, required, group, **context)
|
|
31
|
+
|
|
32
|
+
def cache_any_permission_check(user, *str_perms):
|
|
33
|
+
return any_permission_check(user, *str_perms)
|
oxutils/permissions/perms.py
CHANGED
|
@@ -45,6 +45,112 @@ class ScopePermission(BasePermission):
|
|
|
45
45
|
return str_check(request.user, self.perm, **self.ctx)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
class ScopeAnyPermission(BasePermission):
|
|
49
|
+
"""
|
|
50
|
+
Permission class for checking if user has at least one of multiple permissions.
|
|
51
|
+
|
|
52
|
+
Vérifie si l'utilisateur possède au moins une des permissions fournies.
|
|
53
|
+
Utilise any_permission_check pour une vérification optimisée en une seule requête.
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
@api_controller('/articles', permissions=[
|
|
57
|
+
ScopeAnyPermission('articles:r', 'articles:w:staff', 'articles:d:admin')
|
|
58
|
+
])
|
|
59
|
+
class ArticleController:
|
|
60
|
+
# User needs either read access, OR staff write access, OR admin delete access
|
|
61
|
+
pass
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, *perms: str):
|
|
65
|
+
"""
|
|
66
|
+
Initialize the permission checker with multiple permission strings.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
*perms: Variable number of permission strings in format "<scope>:<actions>:<group>?context"
|
|
70
|
+
"""
|
|
71
|
+
if not perms:
|
|
72
|
+
raise ValueError("At least one permission string must be provided")
|
|
73
|
+
self.perms = perms
|
|
74
|
+
|
|
75
|
+
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
|
76
|
+
"""
|
|
77
|
+
Check if the user has at least one of the required permissions.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
request: HTTP request object
|
|
81
|
+
controller: Controller instance
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
True if user has at least one permission, False otherwise
|
|
85
|
+
"""
|
|
86
|
+
from oxutils.permissions.caches import cache_any_permission_check
|
|
87
|
+
return cache_any_permission_check(request.user, *self.perms)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ScopeAnyActionPermission(BasePermission):
|
|
91
|
+
"""
|
|
92
|
+
Permission class for checking if user has at least one of multiple actions on a scope.
|
|
93
|
+
|
|
94
|
+
Vérifie si l'utilisateur possède au moins une des actions requises pour un scope donné.
|
|
95
|
+
La chaîne d'actions contient plusieurs actions dont au moins une est requise.
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
@api_controller('/articles', permissions=[
|
|
99
|
+
ScopeAnyActionPermission('articles:rwd:staff')
|
|
100
|
+
])
|
|
101
|
+
class ArticleController:
|
|
102
|
+
# User needs read OR write OR delete access on articles in staff group
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
@api_controller('/invoices', permissions=[
|
|
106
|
+
ScopeAnyActionPermission('invoices:rw?tenant_id=123')
|
|
107
|
+
])
|
|
108
|
+
class InvoiceController:
|
|
109
|
+
# User needs read OR write access on invoices with tenant_id=123
|
|
110
|
+
pass
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, perm: str, ctx: Optional[dict] = None):
|
|
114
|
+
"""
|
|
115
|
+
Initialize the permission checker with a permission string.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
perm: Permission string in format "<scope>:<actions>:<group>?context"
|
|
119
|
+
where actions contains multiple characters (e.g., 'rwd' for read OR write OR delete)
|
|
120
|
+
ctx: Optional additional context dict
|
|
121
|
+
"""
|
|
122
|
+
if not perm:
|
|
123
|
+
raise ValueError("Permission string must be provided")
|
|
124
|
+
|
|
125
|
+
self.perm = perm
|
|
126
|
+
self.ctx = ctx if ctx else dict()
|
|
127
|
+
|
|
128
|
+
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
|
129
|
+
"""
|
|
130
|
+
Check if the user has at least one of the required actions.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
request: HTTP request object
|
|
134
|
+
controller: Controller instance
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
True if user has at least one action, False otherwise
|
|
138
|
+
"""
|
|
139
|
+
from oxutils.permissions.caches import cache_any_action_check
|
|
140
|
+
from oxutils.permissions.utils import parse_permission
|
|
141
|
+
|
|
142
|
+
scope, actions, group, query_context = parse_permission(self.perm)
|
|
143
|
+
final_context = {**query_context, **self.ctx}
|
|
144
|
+
|
|
145
|
+
return cache_any_action_check(
|
|
146
|
+
request.user,
|
|
147
|
+
scope,
|
|
148
|
+
actions,
|
|
149
|
+
group,
|
|
150
|
+
**final_context
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
48
154
|
def access_manager(actions: str):
|
|
49
155
|
"""
|
|
50
156
|
Factory function for creating ScopePermission instances for access manager.
|