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.
Files changed (68) hide show
  1. oxutils/__init__.py +2 -2
  2. oxutils/audit/migrations/0001_initial.py +2 -2
  3. oxutils/audit/models.py +2 -2
  4. oxutils/constants.py +6 -0
  5. oxutils/jwt/auth.py +150 -1
  6. oxutils/jwt/models.py +81 -0
  7. oxutils/jwt/tokens.py +69 -0
  8. oxutils/jwt/utils.py +45 -0
  9. oxutils/logger/__init__.py +10 -0
  10. oxutils/logger/receivers.py +10 -6
  11. oxutils/logger/settings.py +2 -2
  12. oxutils/models/base.py +102 -0
  13. oxutils/models/fields.py +79 -0
  14. oxutils/oxiliere/apps.py +9 -1
  15. oxutils/oxiliere/authorization.py +45 -0
  16. oxutils/oxiliere/caches.py +13 -11
  17. oxutils/oxiliere/checks.py +31 -0
  18. oxutils/oxiliere/constants.py +3 -0
  19. oxutils/oxiliere/context.py +16 -0
  20. oxutils/oxiliere/exceptions.py +16 -0
  21. oxutils/oxiliere/management/commands/grant_tenant_owners.py +19 -0
  22. oxutils/oxiliere/management/commands/init_oxiliere_system.py +30 -11
  23. oxutils/oxiliere/middleware.py +65 -11
  24. oxutils/oxiliere/models.py +146 -9
  25. oxutils/oxiliere/permissions.py +28 -35
  26. oxutils/oxiliere/schemas.py +16 -6
  27. oxutils/oxiliere/signals.py +5 -0
  28. oxutils/oxiliere/utils.py +36 -1
  29. oxutils/pagination/cursor.py +367 -0
  30. oxutils/permissions/__init__.py +0 -0
  31. oxutils/permissions/actions.py +57 -0
  32. oxutils/permissions/admin.py +3 -0
  33. oxutils/permissions/apps.py +10 -0
  34. oxutils/permissions/caches.py +33 -0
  35. oxutils/permissions/checks.py +188 -0
  36. oxutils/permissions/constants.py +0 -0
  37. oxutils/permissions/controllers.py +344 -0
  38. oxutils/permissions/exceptions.py +60 -0
  39. oxutils/permissions/management/__init__.py +0 -0
  40. oxutils/permissions/management/commands/__init__.py +0 -0
  41. oxutils/permissions/management/commands/load_permission_preset.py +112 -0
  42. oxutils/permissions/migrations/0001_initial.py +112 -0
  43. oxutils/permissions/migrations/0002_alter_grant_role.py +19 -0
  44. oxutils/permissions/migrations/0003_alter_grant_options_alter_group_options_and_more.py +33 -0
  45. oxutils/permissions/migrations/__init__.py +0 -0
  46. oxutils/permissions/models.py +171 -0
  47. oxutils/permissions/perms.py +201 -0
  48. oxutils/permissions/queryset.py +92 -0
  49. oxutils/permissions/schemas.py +276 -0
  50. oxutils/permissions/services.py +663 -0
  51. oxutils/permissions/tests.py +3 -0
  52. oxutils/permissions/utils.py +784 -0
  53. oxutils/settings.py +14 -194
  54. oxutils/users/apps.py +1 -1
  55. oxutils/users/migrations/0001_initial.py +47 -0
  56. oxutils/users/migrations/0002_alter_user_first_name_alter_user_last_name.py +23 -0
  57. oxutils/users/migrations/0003_user_photo.py +18 -0
  58. oxutils/users/models.py +3 -0
  59. oxutils/utils.py +25 -0
  60. {oxutils-0.1.6.dist-info → oxutils-0.1.14.dist-info}/METADATA +14 -11
  61. oxutils-0.1.14.dist-info/RECORD +123 -0
  62. oxutils/jwt/client.py +0 -123
  63. oxutils/jwt/constants.py +0 -1
  64. oxutils/s3/settings.py +0 -34
  65. oxutils/s3/storages.py +0 -130
  66. oxutils-0.1.6.dist-info/RECORD +0 -88
  67. /oxutils/{s3 → pagination}/__init__.py +0 -0
  68. {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"