oxutils 0.1.12__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 +1 -1
- 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.14.dist-info}/METADATA +1 -1
- {oxutils-0.1.12.dist-info → oxutils-0.1.14.dist-info}/RECORD +9 -8
- {oxutils-0.1.12.dist-info → oxutils-0.1.14.dist-info}/WHEEL +0 -0
oxutils/__init__.py
CHANGED
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.
|
oxutils/permissions/utils.py
CHANGED
|
@@ -387,41 +387,156 @@ def check(
|
|
|
387
387
|
# Vérifier l'existence d'un grant correspondant
|
|
388
388
|
return Grant.objects.filter(grant_filter).exists()
|
|
389
389
|
|
|
390
|
-
|
|
390
|
+
|
|
391
|
+
def any_action_check(
|
|
392
|
+
user: AbstractBaseUser,
|
|
393
|
+
scope: str,
|
|
394
|
+
required: list[str],
|
|
395
|
+
group: Optional[str] = None,
|
|
396
|
+
**context: Any
|
|
397
|
+
) -> bool:
|
|
391
398
|
"""
|
|
392
|
-
Vérifie si un utilisateur possède
|
|
399
|
+
Vérifie si un utilisateur possède au moins une des actions requises pour un scope donné.
|
|
400
|
+
|
|
401
|
+
Cette fonction utilise une seule requête optimisée avec des conditions OR pour vérifier
|
|
402
|
+
si l'utilisateur possède au moins une des actions dans la liste.
|
|
393
403
|
|
|
394
404
|
Args:
|
|
395
405
|
user: L'utilisateur dont on vérifie les permissions
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
- query params: (Optionnel) Contexte sous forme de query parameters
|
|
401
|
-
**context: Contexte additionnel pour filtrer les grants (fusionné avec les query params)
|
|
406
|
+
scope: Le scope à vérifier (ex: 'articles', 'invoices')
|
|
407
|
+
required: Liste des actions dont au moins une est requise (ex: ['r', 'w'], ['d'])
|
|
408
|
+
group: Slug du groupe optionnel pour filtrer les grants par groupe
|
|
409
|
+
**context: Contexte additionnel pour filtrer les grants (clés JSON)
|
|
402
410
|
|
|
403
411
|
Returns:
|
|
404
|
-
True si l'utilisateur possède
|
|
412
|
+
True si l'utilisateur possède au moins une des actions requises, False sinon
|
|
405
413
|
|
|
406
414
|
Example:
|
|
407
|
-
>>> # Vérifier
|
|
408
|
-
>>>
|
|
409
|
-
True
|
|
410
|
-
>>> # Vérifier écriture sur articles dans le groupe staff
|
|
411
|
-
>>> str_check(user, 'articles:w:staff')
|
|
415
|
+
>>> # Vérifier si l'utilisateur peut lire OU écrire les articles
|
|
416
|
+
>>> any_action_check(user, 'articles', ['r', 'w'])
|
|
412
417
|
True
|
|
413
|
-
>>> #
|
|
414
|
-
>>>
|
|
418
|
+
>>> # Vérifier avec contexte
|
|
419
|
+
>>> any_action_check(user, 'articles', ['w', 'd'], tenant_id=123)
|
|
415
420
|
False
|
|
416
|
-
>>> #
|
|
417
|
-
>>>
|
|
421
|
+
>>> # Vérifier dans le contexte d'un groupe spécifique
|
|
422
|
+
>>> any_action_check(user, 'articles', ['r', 'w'], group='staff')
|
|
418
423
|
True
|
|
419
|
-
|
|
420
|
-
|
|
424
|
+
|
|
425
|
+
Note:
|
|
426
|
+
Les actions sont automatiquement expandées lors de la création du grant,
|
|
427
|
+
donc si un grant contient ['w'], il contient aussi ['r'] implicitement.
|
|
428
|
+
Cette fonction vérifie si AU MOINS UNE des actions requises est présente.
|
|
429
|
+
"""
|
|
430
|
+
# Construire le filtre de base pour l'utilisateur et le scope
|
|
431
|
+
grant_filter = Q(user__pk=user.pk, scope=scope)
|
|
432
|
+
|
|
433
|
+
# Filtrer par groupe si spécifié
|
|
434
|
+
if group:
|
|
435
|
+
grant_filter &= Q(user_group__group__slug=group)
|
|
436
|
+
|
|
437
|
+
# Ajouter les filtres de contexte si fournis
|
|
438
|
+
if context:
|
|
439
|
+
grant_filter &= Q(context__contains=context)
|
|
440
|
+
|
|
441
|
+
# Vérifier si au moins une des actions requises est présente dans le grant
|
|
442
|
+
# Utilise l'opérateur overlap (&&) pour une requête optimale
|
|
443
|
+
grant_filter &= Q(actions__overlap=required)
|
|
444
|
+
|
|
445
|
+
# Vérifier l'existence d'un grant correspondant
|
|
446
|
+
return Grant.objects.filter(grant_filter).exists()
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def any_permission_check(user: AbstractBaseUser, *str_perms: str) -> bool:
|
|
450
|
+
"""
|
|
451
|
+
Vérifie si un utilisateur possède au moins une des permissions fournies.
|
|
452
|
+
|
|
453
|
+
Cette fonction parse toutes les permissions fournies et effectue une seule requête
|
|
454
|
+
optimisée avec des conditions OR pour vérifier si l'utilisateur possède au moins
|
|
455
|
+
une des permissions.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
user: L'utilisateur dont on vérifie les permissions
|
|
459
|
+
*str_perms: Liste de chaînes de permissions au format standard
|
|
460
|
+
(ex: 'articles:r', 'invoices:w:staff', 'users:d?tenant_id=123')
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
True si l'utilisateur possède au moins une des permissions, False sinon
|
|
464
|
+
|
|
465
|
+
Example:
|
|
466
|
+
>>> # Vérifier si l'utilisateur peut lire les articles OU écrire les factures
|
|
467
|
+
>>> any_permission_check(user, 'articles:r', 'invoices:w')
|
|
468
|
+
True
|
|
469
|
+
>>> # Avec différents groupes et contextes
|
|
470
|
+
>>> any_permission_check(
|
|
471
|
+
... user,
|
|
472
|
+
... 'articles:w:staff',
|
|
473
|
+
... 'invoices:r:admin',
|
|
474
|
+
... 'users:d?tenant_id=123'
|
|
475
|
+
... )
|
|
421
476
|
False
|
|
477
|
+
|
|
478
|
+
Note:
|
|
479
|
+
Toute la vérification se fait au niveau de la base de données avec une seule
|
|
480
|
+
requête utilisant des conditions OR pour optimiser les performances.
|
|
422
481
|
"""
|
|
423
|
-
|
|
482
|
+
if not str_perms:
|
|
483
|
+
return False
|
|
484
|
+
|
|
485
|
+
# Construire le filtre de base pour l'utilisateur
|
|
486
|
+
base_filter = Q(user__pk=user.pk)
|
|
487
|
+
|
|
488
|
+
# Construire les conditions OR pour chaque permission
|
|
489
|
+
permission_filters = Q()
|
|
490
|
+
|
|
491
|
+
for perm in str_perms:
|
|
492
|
+
# Parser la permission
|
|
493
|
+
scope, actions, group, context = parse_permission(perm)
|
|
494
|
+
|
|
495
|
+
# Construire le filtre pour cette permission spécifique
|
|
496
|
+
perm_filter = Q(scope=scope, actions__overlap=actions)
|
|
497
|
+
|
|
498
|
+
# Ajouter le filtre de groupe si spécifié
|
|
499
|
+
if group:
|
|
500
|
+
perm_filter &= Q(user_group__group__slug=group)
|
|
501
|
+
|
|
502
|
+
# Ajouter le filtre de contexte si fourni
|
|
503
|
+
if context:
|
|
504
|
+
perm_filter &= Q(context__contains=context)
|
|
505
|
+
|
|
506
|
+
# Ajouter cette permission aux conditions OR
|
|
507
|
+
permission_filters |= perm_filter
|
|
508
|
+
|
|
509
|
+
# Combiner le filtre de base avec les conditions OR et vérifier l'existence
|
|
510
|
+
return Grant.objects.filter(base_filter & permission_filters).exists()
|
|
511
|
+
|
|
424
512
|
|
|
513
|
+
def parse_permission(perm: str) -> tuple[str, list[str], Optional[str], dict[str, Any]]:
|
|
514
|
+
"""
|
|
515
|
+
Parse une chaîne de permission et retourne ses composants.
|
|
516
|
+
|
|
517
|
+
Args:
|
|
518
|
+
perm: Chaîne de permission au format "<scope>:<actions>:<group>?key=value&key2=value2"
|
|
519
|
+
- scope: Le scope (ex: 'articles')
|
|
520
|
+
- actions: Actions requises (ex: 'rw', 'r', 'rwdx')
|
|
521
|
+
- group: (Optionnel) Slug du groupe
|
|
522
|
+
- query params: (Optionnel) Contexte sous forme de query parameters
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
Tuple contenant (scope, actions_list, group, context_dict)
|
|
526
|
+
|
|
527
|
+
Raises:
|
|
528
|
+
ValueError: Si le format de la permission est invalide
|
|
529
|
+
|
|
530
|
+
Example:
|
|
531
|
+
>>> parse_permission('articles:rw')
|
|
532
|
+
('articles', ['r', 'w'], None, {})
|
|
533
|
+
>>> parse_permission('articles:w:staff')
|
|
534
|
+
('articles', ['w'], 'staff', {})
|
|
535
|
+
>>> parse_permission('articles:rw?tenant_id=123&status=published')
|
|
536
|
+
('articles', ['r', 'w'], None, {'tenant_id': 123, 'status': 'published'})
|
|
537
|
+
>>> parse_permission('articles:w:staff?tenant_id=123')
|
|
538
|
+
('articles', ['w'], 'staff', {'tenant_id': 123})
|
|
539
|
+
"""
|
|
425
540
|
# Séparer la partie principale des query params
|
|
426
541
|
if '?' in perm:
|
|
427
542
|
from urllib.parse import parse_qs
|
|
@@ -455,7 +570,48 @@ def str_check(user: AbstractBaseUser, perm: str, **context: Any) -> bool:
|
|
|
455
570
|
|
|
456
571
|
# Convertir la chaîne d'actions en liste
|
|
457
572
|
# 'rwd' -> ['r', 'w', 'd']
|
|
458
|
-
|
|
573
|
+
actions_list = list(actions_str)
|
|
574
|
+
|
|
575
|
+
return scope, actions_list, group, query_context
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def str_check(user: AbstractBaseUser, perm: str, **context: Any) -> bool:
|
|
579
|
+
"""
|
|
580
|
+
Vérifie si un utilisateur possède les permissions requises à partir d'une chaîne formatée.
|
|
581
|
+
|
|
582
|
+
Args:
|
|
583
|
+
user: L'utilisateur dont on vérifie les permissions
|
|
584
|
+
perm: Chaîne de permission au format "<scope>:<actions>:<group>?key=value&key2=value2"
|
|
585
|
+
- scope: Le scope à vérifier (ex: 'articles')
|
|
586
|
+
- actions: Actions requises (ex: 'rw', 'r', 'rwdx')
|
|
587
|
+
- group: (Optionnel) Slug du groupe
|
|
588
|
+
- query params: (Optionnel) Contexte sous forme de query parameters
|
|
589
|
+
**context: Contexte additionnel pour filtrer les grants (fusionné avec les query params)
|
|
590
|
+
|
|
591
|
+
Returns:
|
|
592
|
+
True si l'utilisateur possède les permissions requises, False sinon
|
|
593
|
+
|
|
594
|
+
Example:
|
|
595
|
+
>>> # Vérifier lecture sur articles
|
|
596
|
+
>>> str_check(user, 'articles:r')
|
|
597
|
+
True
|
|
598
|
+
>>> # Vérifier écriture sur articles dans le groupe staff
|
|
599
|
+
>>> str_check(user, 'articles:w:staff')
|
|
600
|
+
True
|
|
601
|
+
>>> # Avec contexte via query params
|
|
602
|
+
>>> str_check(user, 'articles:w?tenant_id=123&status=published')
|
|
603
|
+
False
|
|
604
|
+
>>> # Avec groupe et contexte
|
|
605
|
+
>>> str_check(user, 'articles:w:staff?tenant_id=123')
|
|
606
|
+
True
|
|
607
|
+
>>> # Contexte mixte (query params + kwargs)
|
|
608
|
+
>>> str_check(user, 'articles:w?tenant_id=123', level=2)
|
|
609
|
+
False
|
|
610
|
+
"""
|
|
611
|
+
from .caches import cache_check
|
|
612
|
+
|
|
613
|
+
# Parser la chaîne de permission
|
|
614
|
+
scope, required, group, query_context = parse_permission(perm)
|
|
459
615
|
|
|
460
616
|
# Fusionner les contextes (kwargs ont priorité sur query params)
|
|
461
617
|
final_context = {**query_context, **context}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 5.2.9 on 2026-02-02 13:03
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0002_alter_user_first_name_alter_user_last_name'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='user',
|
|
15
|
+
name='photo',
|
|
16
|
+
field=models.ImageField(blank=True, null=True, upload_to='users/'),
|
|
17
|
+
),
|
|
18
|
+
]
|
oxutils/users/models.py
CHANGED
|
@@ -59,6 +59,7 @@ class User(AbstractUser, SafeDeleteModel, BaseModelMixin):
|
|
|
59
59
|
email = models.EmailField(unique=True)
|
|
60
60
|
first_name = models.CharField(max_length=255, blank=True, null=True)
|
|
61
61
|
last_name = models.CharField(max_length=255, blank=True, null=True)
|
|
62
|
+
photo = models.ImageField(upload_to='users/', blank=True, null=True)
|
|
62
63
|
is_active = models.BooleanField(default=True)
|
|
63
64
|
subscription_plan = models.CharField(max_length=255, null=True, blank=True)
|
|
64
65
|
subscription_status = models.CharField(max_length=255, null=True, blank=True)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
oxutils/__init__.py,sha256=
|
|
1
|
+
oxutils/__init__.py,sha256=4VMJ48vLu5PsZMfhvnI84bZ3Bvx7f1p2lQKUz6Boyuc,508
|
|
2
2
|
oxutils/apps.py,sha256=8pO8eXUZeKYn8fPo0rkoytmHACwDNuTNhdRcpkPTxGM,347
|
|
3
3
|
oxutils/audit/__init__.py,sha256=uonc00G73Xm7RwRHVWD-wBn8lJYNCq3iBgnRGMWAEWs,583
|
|
4
4
|
oxutils/audit/apps.py,sha256=xvnmB5Z6nLV7ejzhSeQbesTkwRoFygoPFob8H5QTHgU,304
|
|
@@ -85,7 +85,7 @@ oxutils/permissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
85
85
|
oxutils/permissions/actions.py,sha256=YmwiKOxhHl6GJ9YxrCzftWfd1ddrYR2GSZx4VeLtv5g,1273
|
|
86
86
|
oxutils/permissions/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
|
|
87
87
|
oxutils/permissions/apps.py,sha256=nuTziz75_t-GToyZpJv2uloEDxzoz-v8ZBglzzdQeG8,272
|
|
88
|
-
oxutils/permissions/caches.py,sha256=
|
|
88
|
+
oxutils/permissions/caches.py,sha256=OVkbi3oQGXyotFVqTxt0jHn7zXncZFCX-gnDNbB4uec,1262
|
|
89
89
|
oxutils/permissions/checks.py,sha256=eNnm2RF0IcZwzdpQqsoEdY9684tQ0r4I839DIFoQfRQ,6590
|
|
90
90
|
oxutils/permissions/constants.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
91
|
oxutils/permissions/controllers.py,sha256=W9iOsjWPPXUX9c1ZR_v3aOEpT_ZEztQbICQ_KcXQU70,9966
|
|
@@ -98,12 +98,12 @@ oxutils/permissions/migrations/0002_alter_grant_role.py,sha256=uTWAEgCYszHGw3fKZ
|
|
|
98
98
|
oxutils/permissions/migrations/0003_alter_grant_options_alter_group_options_and_more.py,sha256=W5QoxO2klWnze9p0yV9WUbLPIodZoc9CRcrihg6c6RI,893
|
|
99
99
|
oxutils/permissions/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
100
|
oxutils/permissions/models.py,sha256=FzTsecGcBdlZU6SPlT-aZmbocFDxJ0pWsHVTOvtQvUo,4474
|
|
101
|
-
oxutils/permissions/perms.py,sha256=
|
|
101
|
+
oxutils/permissions/perms.py,sha256=HUrXoVIC4vry1J4tnOdEVSD7AvXovylFR3BBSVMhH48,6991
|
|
102
102
|
oxutils/permissions/queryset.py,sha256=c_iYIO5ZX4jp3lWP9WucloAr2RPA72XuI_OM4SNhKpw,3173
|
|
103
103
|
oxutils/permissions/schemas.py,sha256=Y6lwP9FkxSmklhwq5_VL6zORxvTmUXShsClm_FM2B-w,6122
|
|
104
104
|
oxutils/permissions/services.py,sha256=d0oCeluzU_7YHpllphMymm-6gY_joKK-gxNDfJ_rD-Q,22140
|
|
105
105
|
oxutils/permissions/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
|
|
106
|
-
oxutils/permissions/utils.py,sha256=
|
|
106
|
+
oxutils/permissions/utils.py,sha256=nyAvWOmkEo4XHVSzd0EEoSoOr-PX0oHD_w7J7fmGkZU,28085
|
|
107
107
|
oxutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
108
|
oxutils/settings.py,sha256=EZOHxvlj-RCLlble7il0SUcuOAbPlmxMe8cRgu74xfA,2404
|
|
109
109
|
oxutils/types.py,sha256=DIz8YK8xMpLc7FYbf88yEElyLsYN_-rbvaZXvENQkOQ,234
|
|
@@ -112,11 +112,12 @@ oxutils/users/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
|
|
|
112
112
|
oxutils/users/apps.py,sha256=zfWHq8f0DIh8skbnqskDSoHG9nrvVrCegSz22Mw4BGI,150
|
|
113
113
|
oxutils/users/migrations/0001_initial.py,sha256=7l5xgJnms2D8Nnazh38iBQ7I1W9NgNTF228-og_tOVw,3136
|
|
114
114
|
oxutils/users/migrations/0002_alter_user_first_name_alter_user_last_name.py,sha256=o65pUPXu5m9tX_pHkeyKreRUPrByzQRU803Xz4D8v94,577
|
|
115
|
+
oxutils/users/migrations/0003_user_photo.py,sha256=_YiKl0EDKuWXX28yYYqGqWHaZtidjrR0ddADjMjJMxA,432
|
|
115
116
|
oxutils/users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
|
-
oxutils/users/models.py,sha256=
|
|
117
|
+
oxutils/users/models.py,sha256=RmPwsTbjhnDb0qALQrz_t-ODSj05NfISE9JHxcfv2z4,3178
|
|
117
118
|
oxutils/users/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
|
|
118
119
|
oxutils/users/utils.py,sha256=jY-zL8vLT5U3E2FV3DqCvrPORjKLutbkPZTQ-z96dCw,376
|
|
119
120
|
oxutils/utils.py,sha256=6yGX2d1ajU5RqgfqiaS4McYm7ip2KEgADABo3M-yA3U,595
|
|
120
|
-
oxutils-0.1.
|
|
121
|
-
oxutils-0.1.
|
|
122
|
-
oxutils-0.1.
|
|
121
|
+
oxutils-0.1.14.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
122
|
+
oxutils-0.1.14.dist-info/METADATA,sha256=UXHNH7j-hUIfeM_6Ti9VUJTY0BDlUC5k4HmiR3PE7_0,8389
|
|
123
|
+
oxutils-0.1.14.dist-info/RECORD,,
|
|
File without changes
|