oxutils 0.1.6__py3-none-any.whl → 0.1.14__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 +2 -2
- oxutils/audit/migrations/0001_initial.py +2 -2
- oxutils/audit/models.py +2 -2
- oxutils/constants.py +6 -0
- oxutils/jwt/auth.py +150 -1
- oxutils/jwt/models.py +81 -0
- oxutils/jwt/tokens.py +69 -0
- oxutils/jwt/utils.py +45 -0
- oxutils/logger/__init__.py +10 -0
- oxutils/logger/receivers.py +10 -6
- oxutils/logger/settings.py +2 -2
- oxutils/models/base.py +102 -0
- oxutils/models/fields.py +79 -0
- oxutils/oxiliere/apps.py +9 -1
- oxutils/oxiliere/authorization.py +45 -0
- oxutils/oxiliere/caches.py +13 -11
- oxutils/oxiliere/checks.py +31 -0
- oxutils/oxiliere/constants.py +3 -0
- oxutils/oxiliere/context.py +16 -0
- oxutils/oxiliere/exceptions.py +16 -0
- oxutils/oxiliere/management/commands/grant_tenant_owners.py +19 -0
- oxutils/oxiliere/management/commands/init_oxiliere_system.py +30 -11
- oxutils/oxiliere/middleware.py +65 -11
- oxutils/oxiliere/models.py +146 -9
- oxutils/oxiliere/permissions.py +28 -35
- oxutils/oxiliere/schemas.py +16 -6
- oxutils/oxiliere/signals.py +5 -0
- oxutils/oxiliere/utils.py +36 -1
- oxutils/pagination/cursor.py +367 -0
- oxutils/permissions/__init__.py +0 -0
- oxutils/permissions/actions.py +57 -0
- oxutils/permissions/admin.py +3 -0
- oxutils/permissions/apps.py +10 -0
- oxutils/permissions/caches.py +33 -0
- oxutils/permissions/checks.py +188 -0
- oxutils/permissions/constants.py +0 -0
- oxutils/permissions/controllers.py +344 -0
- oxutils/permissions/exceptions.py +60 -0
- oxutils/permissions/management/__init__.py +0 -0
- oxutils/permissions/management/commands/__init__.py +0 -0
- oxutils/permissions/management/commands/load_permission_preset.py +112 -0
- oxutils/permissions/migrations/0001_initial.py +112 -0
- oxutils/permissions/migrations/0002_alter_grant_role.py +19 -0
- oxutils/permissions/migrations/0003_alter_grant_options_alter_group_options_and_more.py +33 -0
- oxutils/permissions/migrations/__init__.py +0 -0
- oxutils/permissions/models.py +171 -0
- oxutils/permissions/perms.py +201 -0
- oxutils/permissions/queryset.py +92 -0
- oxutils/permissions/schemas.py +276 -0
- oxutils/permissions/services.py +663 -0
- oxutils/permissions/tests.py +3 -0
- oxutils/permissions/utils.py +784 -0
- oxutils/settings.py +14 -194
- oxutils/users/apps.py +1 -1
- oxutils/users/migrations/0001_initial.py +47 -0
- oxutils/users/migrations/0002_alter_user_first_name_alter_user_last_name.py +23 -0
- oxutils/users/migrations/0003_user_photo.py +18 -0
- oxutils/users/models.py +3 -0
- oxutils/utils.py +25 -0
- {oxutils-0.1.6.dist-info → oxutils-0.1.14.dist-info}/METADATA +14 -11
- oxutils-0.1.14.dist-info/RECORD +123 -0
- oxutils/jwt/client.py +0 -123
- oxutils/jwt/constants.py +0 -1
- oxutils/s3/settings.py +0 -34
- oxutils/s3/storages.py +0 -130
- oxutils-0.1.6.dist-info/RECORD +0 -88
- /oxutils/{s3 → pagination}/__init__.py +0 -0
- {oxutils-0.1.6.dist-info → oxutils-0.1.14.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from django.db import models
|
|
3
|
+
from django.db.models import Q
|
|
4
|
+
from django.contrib.auth.models import AbstractBaseUser
|
|
5
|
+
|
|
6
|
+
from .models import Grant
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PermissionQuerySet(models.QuerySet):
|
|
10
|
+
"""
|
|
11
|
+
QuerySet personnalisé pour filtrer des objets selon les permissions d'un utilisateur.
|
|
12
|
+
Permet de filtrer des querysets en fonction des grants de permissions.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def allowed_for(
|
|
16
|
+
self,
|
|
17
|
+
user: AbstractBaseUser,
|
|
18
|
+
scope: str,
|
|
19
|
+
required_actions: list[str],
|
|
20
|
+
**context: Any
|
|
21
|
+
) -> "PermissionQuerySet":
|
|
22
|
+
"""
|
|
23
|
+
Filtre les objets si l'utilisateur a les permissions requises.
|
|
24
|
+
Vérifie l'existence d'un grant valide avant de retourner le queryset.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
user: L'utilisateur dont on vérifie les permissions
|
|
28
|
+
scope: Le scope à vérifier (ex: 'articles', 'users')
|
|
29
|
+
required_actions: Liste des actions requises (ex: ['r'], ['w', 'r'])
|
|
30
|
+
**context: Contexte additionnel pour filtrer (ex: tenant_id=123)
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
QuerySet complet si autorisé, QuerySet vide sinon
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
>>> Article.objects.allowed_for(user, 'articles', ['r'])
|
|
37
|
+
>>> Article.objects.allowed_for(user, 'articles', ['w'], tenant_id=123)
|
|
38
|
+
"""
|
|
39
|
+
# Construire le filtre pour vérifier l'existence d'un grant
|
|
40
|
+
grant_filter = Q(
|
|
41
|
+
user__pk=user.pk,
|
|
42
|
+
scope=scope,
|
|
43
|
+
actions__contains=list(required_actions),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Ajouter les filtres de contexte si fournis
|
|
47
|
+
if context:
|
|
48
|
+
grant_filter &= Q(context__contains=context)
|
|
49
|
+
|
|
50
|
+
# Si un grant existe, retourner le queryset complet, sinon vide
|
|
51
|
+
if Grant.objects.filter(grant_filter).exists():
|
|
52
|
+
return self
|
|
53
|
+
return self.none()
|
|
54
|
+
|
|
55
|
+
def denied_for(
|
|
56
|
+
self,
|
|
57
|
+
user: AbstractBaseUser,
|
|
58
|
+
scope: str,
|
|
59
|
+
required_actions: list[str],
|
|
60
|
+
**context: Any
|
|
61
|
+
) -> "PermissionQuerySet":
|
|
62
|
+
"""
|
|
63
|
+
Filtre les objets si l'utilisateur N'A PAS les permissions requises.
|
|
64
|
+
Inverse de allowed_for.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
user: L'utilisateur dont on vérifie les permissions
|
|
68
|
+
scope: Le scope à vérifier
|
|
69
|
+
required_actions: Liste des actions requises
|
|
70
|
+
**context: Contexte additionnel pour filtrer
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
QuerySet complet si NON autorisé, QuerySet vide si autorisé
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
>>> Article.objects.denied_for(user, 'articles', ['w'])
|
|
77
|
+
"""
|
|
78
|
+
# Construire le filtre pour vérifier l'existence d'un grant
|
|
79
|
+
grant_filter = Q(
|
|
80
|
+
user__pk=user.pk,
|
|
81
|
+
scope=scope,
|
|
82
|
+
actions__contains=list(required_actions),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Ajouter les filtres de contexte si fournis
|
|
86
|
+
if context:
|
|
87
|
+
grant_filter &= Q(context__contains=context)
|
|
88
|
+
|
|
89
|
+
# Si un grant existe, retourner vide, sinon le queryset complet
|
|
90
|
+
if Grant.objects.filter(grant_filter).exists():
|
|
91
|
+
return self.none()
|
|
92
|
+
return self
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from ninja import Schema
|
|
4
|
+
from pydantic import field_validator
|
|
5
|
+
|
|
6
|
+
from .actions import ACTIONS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate_actions_list(actions: list[str]) -> list[str]:
|
|
10
|
+
"""
|
|
11
|
+
Valide qu'une liste d'actions contient uniquement des actions valides.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
actions: Liste des actions à valider
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
La liste d'actions si valide
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
ValueError: Si des actions invalides sont présentes
|
|
21
|
+
"""
|
|
22
|
+
invalid_actions = [a for a in actions if a not in ACTIONS]
|
|
23
|
+
if invalid_actions:
|
|
24
|
+
raise ValueError(
|
|
25
|
+
f"Actions invalides: {invalid_actions}. "
|
|
26
|
+
f"Actions valides: {ACTIONS}"
|
|
27
|
+
)
|
|
28
|
+
return actions
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RoleSchema(Schema):
|
|
32
|
+
"""
|
|
33
|
+
Schéma pour un rôle.
|
|
34
|
+
"""
|
|
35
|
+
slug: str
|
|
36
|
+
name: str
|
|
37
|
+
created_at: datetime
|
|
38
|
+
updated_at: datetime
|
|
39
|
+
|
|
40
|
+
class Config:
|
|
41
|
+
from_attributes = True
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class RoleCreateSchema(Schema):
|
|
45
|
+
"""
|
|
46
|
+
Schéma pour la création d'un rôle.
|
|
47
|
+
"""
|
|
48
|
+
slug: str
|
|
49
|
+
name: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class RoleUpdateSchema(Schema):
|
|
53
|
+
"""
|
|
54
|
+
Schéma pour la mise à jour d'un rôle.
|
|
55
|
+
"""
|
|
56
|
+
name: Optional[str] = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class GroupSchema(Schema):
|
|
60
|
+
"""
|
|
61
|
+
Schéma pour un groupe.
|
|
62
|
+
"""
|
|
63
|
+
slug: str
|
|
64
|
+
name: str
|
|
65
|
+
roles: list[RoleSchema] = []
|
|
66
|
+
created_at: datetime
|
|
67
|
+
updated_at: datetime
|
|
68
|
+
|
|
69
|
+
class Config:
|
|
70
|
+
from_attributes = True
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class GroupCreateSchema(Schema):
|
|
74
|
+
"""
|
|
75
|
+
Schéma pour la création d'un groupe.
|
|
76
|
+
"""
|
|
77
|
+
slug: str
|
|
78
|
+
name: str
|
|
79
|
+
roles: list[str] = []
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class GroupUpdateSchema(Schema):
|
|
83
|
+
"""
|
|
84
|
+
Schéma pour la mise à jour d'un groupe.
|
|
85
|
+
"""
|
|
86
|
+
name: Optional[str] = None
|
|
87
|
+
roles: Optional[list[str]] = None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class RoleGrantSchema(Schema):
|
|
91
|
+
"""
|
|
92
|
+
Schéma pour un role grant.
|
|
93
|
+
"""
|
|
94
|
+
id: int
|
|
95
|
+
role: RoleSchema
|
|
96
|
+
scope: str
|
|
97
|
+
actions: list[str]
|
|
98
|
+
context: dict[str, Any] = {}
|
|
99
|
+
|
|
100
|
+
class Config:
|
|
101
|
+
from_attributes = True
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class RoleGrantCreateSchema(Schema):
|
|
105
|
+
"""
|
|
106
|
+
Schéma pour la création d'un role grant.
|
|
107
|
+
"""
|
|
108
|
+
role: str
|
|
109
|
+
scope: str
|
|
110
|
+
actions: list[str]
|
|
111
|
+
context: dict[str, Any] = {}
|
|
112
|
+
|
|
113
|
+
@field_validator('actions')
|
|
114
|
+
@classmethod
|
|
115
|
+
def validate_actions(cls, v: list[str]) -> list[str]:
|
|
116
|
+
"""Valide que toutes les actions sont valides."""
|
|
117
|
+
return validate_actions_list(v)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class RoleGrantUpdateSchema(Schema):
|
|
121
|
+
"""
|
|
122
|
+
Schéma pour la mise à jour d'un role grant.
|
|
123
|
+
"""
|
|
124
|
+
actions: Optional[list[str]] = None
|
|
125
|
+
context: Optional[dict[str, Any]] = None
|
|
126
|
+
|
|
127
|
+
@field_validator('actions')
|
|
128
|
+
@classmethod
|
|
129
|
+
def validate_actions(cls, v: Optional[list[str]]) -> Optional[list[str]]:
|
|
130
|
+
"""Valide que toutes les actions sont valides."""
|
|
131
|
+
if v is not None:
|
|
132
|
+
return validate_actions_list(v)
|
|
133
|
+
return v
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class GrantSchema(Schema):
|
|
137
|
+
"""
|
|
138
|
+
Schéma pour un grant utilisateur.
|
|
139
|
+
"""
|
|
140
|
+
id: int
|
|
141
|
+
user_id: int
|
|
142
|
+
role: Optional[RoleSchema] = None
|
|
143
|
+
scope: str
|
|
144
|
+
actions: list[str]
|
|
145
|
+
context: dict[str, Any] = {}
|
|
146
|
+
created_at: datetime
|
|
147
|
+
updated_at: datetime
|
|
148
|
+
|
|
149
|
+
class Config:
|
|
150
|
+
from_attributes = True
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class GrantCreateSchema(Schema):
|
|
154
|
+
"""
|
|
155
|
+
Schéma pour la création d'un grant utilisateur.
|
|
156
|
+
"""
|
|
157
|
+
user_id: int
|
|
158
|
+
scope: str
|
|
159
|
+
actions: list[str]
|
|
160
|
+
context: dict[str, Any] = {}
|
|
161
|
+
role: Optional[str] = None
|
|
162
|
+
|
|
163
|
+
@field_validator('actions')
|
|
164
|
+
@classmethod
|
|
165
|
+
def validate_actions(cls, v: list[str]) -> list[str]:
|
|
166
|
+
"""Valide que toutes les actions sont valides."""
|
|
167
|
+
return validate_actions_list(v)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class GrantUpdateSchema(Schema):
|
|
171
|
+
"""
|
|
172
|
+
Schéma pour la mise à jour d'un grant utilisateur.
|
|
173
|
+
"""
|
|
174
|
+
actions: Optional[list[str]] = None
|
|
175
|
+
context: Optional[dict[str, Any]] = None
|
|
176
|
+
role: Optional[str] = None
|
|
177
|
+
|
|
178
|
+
@field_validator('actions')
|
|
179
|
+
@classmethod
|
|
180
|
+
def validate_actions(cls, v: Optional[list[str]]) -> Optional[list[str]]:
|
|
181
|
+
"""Valide que toutes les actions sont valides."""
|
|
182
|
+
if v is not None:
|
|
183
|
+
return validate_actions_list(v)
|
|
184
|
+
return v
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class PermissionCheckSchema(Schema):
|
|
188
|
+
"""
|
|
189
|
+
Schéma pour une requête de vérification de permissions.
|
|
190
|
+
"""
|
|
191
|
+
user_id: int
|
|
192
|
+
scope: str
|
|
193
|
+
required_actions: list[str]
|
|
194
|
+
context: dict[str, Any] = {}
|
|
195
|
+
|
|
196
|
+
@field_validator('required_actions')
|
|
197
|
+
@classmethod
|
|
198
|
+
def validate_actions(cls, v: list[str]) -> list[str]:
|
|
199
|
+
"""Valide que toutes les actions sont valides."""
|
|
200
|
+
return validate_actions_list(v)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class PermissionCheckResponseSchema(Schema):
|
|
204
|
+
"""
|
|
205
|
+
Schéma pour la réponse d'une vérification de permissions.
|
|
206
|
+
"""
|
|
207
|
+
allowed: bool
|
|
208
|
+
user_id: int
|
|
209
|
+
scope: str
|
|
210
|
+
required_actions: list[str]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class AssignRoleSchema(Schema):
|
|
214
|
+
"""
|
|
215
|
+
Schéma pour assigner un rôle à un utilisateur.
|
|
216
|
+
"""
|
|
217
|
+
user_id: int
|
|
218
|
+
role: str
|
|
219
|
+
by_user_id: Optional[int] = None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class RevokeRoleSchema(Schema):
|
|
223
|
+
"""
|
|
224
|
+
Schéma pour révoquer un rôle d'un utilisateur.
|
|
225
|
+
"""
|
|
226
|
+
user_id: int
|
|
227
|
+
role: str
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class AssignGroupSchema(Schema):
|
|
231
|
+
"""
|
|
232
|
+
Schéma pour assigner un groupe à un utilisateur.
|
|
233
|
+
"""
|
|
234
|
+
user_id: int
|
|
235
|
+
group: str
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class RevokeGroupSchema(Schema):
|
|
239
|
+
"""
|
|
240
|
+
Schéma pour révoquer un groupe d'un utilisateur.
|
|
241
|
+
"""
|
|
242
|
+
user_id: int
|
|
243
|
+
group: str
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class OverrideGrantSchema(Schema):
|
|
247
|
+
"""
|
|
248
|
+
Schéma pour modifier un grant en retirant des actions.
|
|
249
|
+
"""
|
|
250
|
+
user_id: int
|
|
251
|
+
scope: str
|
|
252
|
+
remove_actions: list[str]
|
|
253
|
+
|
|
254
|
+
@field_validator('remove_actions')
|
|
255
|
+
@classmethod
|
|
256
|
+
def validate_actions(cls, v: list[str]) -> list[str]:
|
|
257
|
+
"""Valide que toutes les actions sont valides."""
|
|
258
|
+
return validate_actions_list(v)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class GroupSyncResponseSchema(Schema):
|
|
262
|
+
"""
|
|
263
|
+
Schéma pour la réponse de la synchronisation d'un groupe.
|
|
264
|
+
"""
|
|
265
|
+
users_synced: int
|
|
266
|
+
grants_updated: int
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class PresetLoadResponseSchema(Schema):
|
|
270
|
+
"""
|
|
271
|
+
Schéma pour la réponse du chargement d'un preset.
|
|
272
|
+
"""
|
|
273
|
+
roles_created: int
|
|
274
|
+
groups_created: int
|
|
275
|
+
role_grants_created: int
|
|
276
|
+
message: str = "Preset chargé avec succès"
|