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/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=DvkIn9bebbDFzHSk08CPBnzHDrEdursLTV4nkRrTGTI,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
|
|
@@ -33,9 +33,10 @@ oxutils/enums/invoices.py,sha256=E33QGQeutZUqvlovJY0VGDxWUb0i_kdfhEiir1ARKuQ,201
|
|
|
33
33
|
oxutils/exceptions.py,sha256=CCjENOD0of6_noif2ajrpfbBLoG16DWa46iB9_uEe3M,3592
|
|
34
34
|
oxutils/functions.py,sha256=4stHj94VebWX0s1XeWshubMD2v8w8QztTWppbkTE_Gg,3246
|
|
35
35
|
oxutils/jwt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
oxutils/jwt/auth.py,sha256=
|
|
37
|
-
oxutils/jwt/
|
|
38
|
-
oxutils/jwt/
|
|
36
|
+
oxutils/jwt/auth.py,sha256=3OACNYR6Mp5J57QUvu0iCuRuYiT5C49urUuCXqspNLA,7437
|
|
37
|
+
oxutils/jwt/middleware.py,sha256=m81lrpl9fJZ5gGHV_ysPxppd8pJklddFHDmxXRUpyvM,14285
|
|
38
|
+
oxutils/jwt/models.py,sha256=SG4qM2Ix7coopfIXUJ2KCkuC2bMAOuBnIwNhB5MVK14,5026
|
|
39
|
+
oxutils/jwt/tokens.py,sha256=l20XgPK-gUJcVwH8cWFSyYhfAQD70iqbzriRJkrPczo,2268
|
|
39
40
|
oxutils/jwt/utils.py,sha256=Wuy-PnCcUw6MpY6z1Isy2vOx-_u1o6LjUfRJgf_cqbY,1202
|
|
40
41
|
oxutils/locale/fr/LC_MESSAGES/django.po,sha256=APXt_8R99seCWjJyS5ELOawvRLvUqqBT32O252BaG5s,7971
|
|
41
42
|
oxutils/logger/__init__.py,sha256=lhPCC8G1aHZTt-FWRqTWptVrqONln-ty9ufVDeOBHYs,183
|
|
@@ -66,11 +67,11 @@ oxutils/oxiliere/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
|
66
67
|
oxutils/oxiliere/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
68
|
oxutils/oxiliere/management/commands/grant_tenant_owners.py,sha256=U0tc-b677kEFA7KC6xah3Ufbg6qYkW21nRikC0FJRQI,774
|
|
68
69
|
oxutils/oxiliere/management/commands/init_oxiliere_system.py,sha256=7ZmKOwL2TOIaPYBpGEoqcw2XslpG1VikUnTJwpu84Lo,4247
|
|
69
|
-
oxutils/oxiliere/middleware.py,sha256=
|
|
70
|
+
oxutils/oxiliere/middleware.py,sha256=opwBbYMIyTMnytLQbQiKpcS5VzLblJci3iFc9r6X5aU,8142
|
|
70
71
|
oxutils/oxiliere/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
72
|
oxutils/oxiliere/models.py,sha256=dN3q-8W2gUcUra49b3R33o0ZNn3stc-pfkoVMAKR4gE,6260
|
|
72
|
-
oxutils/oxiliere/permissions.py,sha256=
|
|
73
|
-
oxutils/oxiliere/schemas.py,sha256=
|
|
73
|
+
oxutils/oxiliere/permissions.py,sha256=Cz1GfwACxPMayQRVwGIMl_PlWM_f2ukP5VHeTlkmDM0,3566
|
|
74
|
+
oxutils/oxiliere/schemas.py,sha256=mjGyYTwQokAlMiZ0gKa5os_9HqFMp4NLaWpYxCb0yc0,3087
|
|
74
75
|
oxutils/oxiliere/settings.py,sha256=ZuKppEyrucWxvvYC2-wLap4RzKfaEfaRdjJnsNZzpuY,440
|
|
75
76
|
oxutils/oxiliere/signals.py,sha256=il6twTzbmv14SxukLx7dLw2QzuNDyVAsIgHmqbYspjw,97
|
|
76
77
|
oxutils/oxiliere/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
|
|
@@ -85,7 +86,7 @@ oxutils/permissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
85
86
|
oxutils/permissions/actions.py,sha256=YmwiKOxhHl6GJ9YxrCzftWfd1ddrYR2GSZx4VeLtv5g,1273
|
|
86
87
|
oxutils/permissions/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
|
|
87
88
|
oxutils/permissions/apps.py,sha256=nuTziz75_t-GToyZpJv2uloEDxzoz-v8ZBglzzdQeG8,272
|
|
88
|
-
oxutils/permissions/caches.py,sha256=
|
|
89
|
+
oxutils/permissions/caches.py,sha256=OVkbi3oQGXyotFVqTxt0jHn7zXncZFCX-gnDNbB4uec,1262
|
|
89
90
|
oxutils/permissions/checks.py,sha256=eNnm2RF0IcZwzdpQqsoEdY9684tQ0r4I839DIFoQfRQ,6590
|
|
90
91
|
oxutils/permissions/constants.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
92
|
oxutils/permissions/controllers.py,sha256=W9iOsjWPPXUX9c1ZR_v3aOEpT_ZEztQbICQ_KcXQU70,9966
|
|
@@ -98,12 +99,12 @@ oxutils/permissions/migrations/0002_alter_grant_role.py,sha256=uTWAEgCYszHGw3fKZ
|
|
|
98
99
|
oxutils/permissions/migrations/0003_alter_grant_options_alter_group_options_and_more.py,sha256=W5QoxO2klWnze9p0yV9WUbLPIodZoc9CRcrihg6c6RI,893
|
|
99
100
|
oxutils/permissions/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
101
|
oxutils/permissions/models.py,sha256=FzTsecGcBdlZU6SPlT-aZmbocFDxJ0pWsHVTOvtQvUo,4474
|
|
101
|
-
oxutils/permissions/perms.py,sha256=
|
|
102
|
+
oxutils/permissions/perms.py,sha256=HUrXoVIC4vry1J4tnOdEVSD7AvXovylFR3BBSVMhH48,6991
|
|
102
103
|
oxutils/permissions/queryset.py,sha256=c_iYIO5ZX4jp3lWP9WucloAr2RPA72XuI_OM4SNhKpw,3173
|
|
103
104
|
oxutils/permissions/schemas.py,sha256=Y6lwP9FkxSmklhwq5_VL6zORxvTmUXShsClm_FM2B-w,6122
|
|
104
105
|
oxutils/permissions/services.py,sha256=d0oCeluzU_7YHpllphMymm-6gY_joKK-gxNDfJ_rD-Q,22140
|
|
105
106
|
oxutils/permissions/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
|
|
106
|
-
oxutils/permissions/utils.py,sha256=
|
|
107
|
+
oxutils/permissions/utils.py,sha256=nyAvWOmkEo4XHVSzd0EEoSoOr-PX0oHD_w7J7fmGkZU,28085
|
|
107
108
|
oxutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
109
|
oxutils/settings.py,sha256=EZOHxvlj-RCLlble7il0SUcuOAbPlmxMe8cRgu74xfA,2404
|
|
109
110
|
oxutils/types.py,sha256=DIz8YK8xMpLc7FYbf88yEElyLsYN_-rbvaZXvENQkOQ,234
|
|
@@ -112,11 +113,12 @@ oxutils/users/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
|
|
|
112
113
|
oxutils/users/apps.py,sha256=zfWHq8f0DIh8skbnqskDSoHG9nrvVrCegSz22Mw4BGI,150
|
|
113
114
|
oxutils/users/migrations/0001_initial.py,sha256=7l5xgJnms2D8Nnazh38iBQ7I1W9NgNTF228-og_tOVw,3136
|
|
114
115
|
oxutils/users/migrations/0002_alter_user_first_name_alter_user_last_name.py,sha256=o65pUPXu5m9tX_pHkeyKreRUPrByzQRU803Xz4D8v94,577
|
|
116
|
+
oxutils/users/migrations/0003_user_photo.py,sha256=_YiKl0EDKuWXX28yYYqGqWHaZtidjrR0ddADjMjJMxA,432
|
|
115
117
|
oxutils/users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
|
-
oxutils/users/models.py,sha256=
|
|
118
|
+
oxutils/users/models.py,sha256=RmPwsTbjhnDb0qALQrz_t-ODSj05NfISE9JHxcfv2z4,3178
|
|
117
119
|
oxutils/users/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
|
|
118
120
|
oxutils/users/utils.py,sha256=jY-zL8vLT5U3E2FV3DqCvrPORjKLutbkPZTQ-z96dCw,376
|
|
119
121
|
oxutils/utils.py,sha256=6yGX2d1ajU5RqgfqiaS4McYm7ip2KEgADABo3M-yA3U,595
|
|
120
|
-
oxutils-0.1.
|
|
121
|
-
oxutils-0.1.
|
|
122
|
-
oxutils-0.1.
|
|
122
|
+
oxutils-0.1.15.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
123
|
+
oxutils-0.1.15.dist-info/METADATA,sha256=_EDraKohaULS5QhUbtjTRhkka7-cFy-Jny-G-WMZr4E,8389
|
|
124
|
+
oxutils-0.1.15.dist-info/RECORD,,
|
|
File without changes
|