maquinaweb-shared-auth 0.2.0__py3-none-any.whl → 0.2.2__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.
Potentially problematic release.
This version of maquinaweb-shared-auth might be problematic. Click here for more details.
- {maquinaweb_shared_auth-0.2.0.dist-info → maquinaweb_shared_auth-0.2.2.dist-info}/METADATA +1 -1
- maquinaweb_shared_auth-0.2.2.dist-info/RECORD +18 -0
- shared_auth/authentication.py +5 -6
- shared_auth/decorators.py +3 -5
- shared_auth/managers.py +15 -24
- shared_auth/middleware.py +6 -12
- shared_auth/mixins.py +15 -17
- shared_auth/models.py +1 -7
- shared_auth/permissions.py +1 -3
- maquinaweb_shared_auth-0.2.0.dist-info/RECORD +0 -18
- {maquinaweb_shared_auth-0.2.0.dist-info → maquinaweb_shared_auth-0.2.2.dist-info}/WHEEL +0 -0
- {maquinaweb_shared_auth-0.2.0.dist-info → maquinaweb_shared_auth-0.2.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
shared_auth/__init__.py,sha256=Ta8lIbyn22J6wU-TgWeSvdDHS4NBRf4Sv1Ag8VtY-bE,152
|
|
2
|
+
shared_auth/app.py,sha256=EHoLKlpW41o6ZxcH184aMhnWQxkVp9fH2_-89RjMz-4,215
|
|
3
|
+
shared_auth/authentication.py,sha256=-9FTdSBvfjq7vgZbJBvoJyPcwN-zIfx1pRhgPFB9dnw,1450
|
|
4
|
+
shared_auth/conf.py,sha256=-jdnCokMvWvVKllfsxNYCsPk1Vo3MDRq4Y1MsO16oeA,411
|
|
5
|
+
shared_auth/decorators.py,sha256=A2BVcEk1Ty4M4ZOAa_fqEdPIn_w9UXxIvfJWFlqw3wk,3255
|
|
6
|
+
shared_auth/exceptions.py,sha256=eiII-REupK6GeFinisteYO3FsGUDAN5zAajXPhTREm8,404
|
|
7
|
+
shared_auth/fields.py,sha256=RAcmFh1D_nkbai_7t_OrPZhfhAipesy5kKnEj4LUvvM,1254
|
|
8
|
+
shared_auth/managers.py,sha256=Oskz863DGG7nFp6ijZfYVWGCG-Inp6PAP0NakX-x71g,6812
|
|
9
|
+
shared_auth/middleware.py,sha256=xVoSnlndp6-_cgQADASk2ie8th8e_7SN_7q-lfArx9Y,6124
|
|
10
|
+
shared_auth/mixins.py,sha256=-D-mDmDI_EjMGpUpkMEp1XTlecvUlCRj-Bch9dm9s_I,7400
|
|
11
|
+
shared_auth/models.py,sha256=feSWcil3nDEOAm_T4kTZSRLFmtmsuGjAadoKIpyUYAI,5159
|
|
12
|
+
shared_auth/permissions.py,sha256=CTsd0xE_Z-LYNVwU4jOJLWvlcbdRG8mLkLMEnbmkhJA,2665
|
|
13
|
+
shared_auth/router.py,sha256=zYidJ7j40lQLrhkCtAQAp-rQLhua_UF0X7SDzYRcV5w,668
|
|
14
|
+
shared_auth/serializers.py,sha256=TDpuZVsOL-6igINSOOOyELWbTUeet4XWRoBkvcMGjW4,4290
|
|
15
|
+
maquinaweb_shared_auth-0.2.2.dist-info/METADATA,sha256=yo7CjlyCwraTwb__Ztji-Vq_WZklbf_D4UMrgNJhmPk,27150
|
|
16
|
+
maquinaweb_shared_auth-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
+
maquinaweb_shared_auth-0.2.2.dist-info/top_level.txt,sha256=msyYRy02ZV7zz7GR1raUI5LXGFIFn2TIkgkeKZqKufE,12
|
|
18
|
+
maquinaweb_shared_auth-0.2.2.dist-info/RECORD,,
|
shared_auth/authentication.py
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
Backend de autenticação usando tokens do banco compartilhado
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from rest_framework.authentication import TokenAuthentication
|
|
6
|
-
from rest_framework import exceptions
|
|
7
5
|
from django.utils.translation import gettext_lazy as _
|
|
6
|
+
from rest_framework import exceptions
|
|
7
|
+
from rest_framework.authentication import TokenAuthentication
|
|
8
|
+
|
|
8
9
|
from .models import SharedToken, SharedUser
|
|
9
10
|
|
|
10
11
|
|
|
@@ -27,15 +28,13 @@ class SharedTokenAuthentication(TokenAuthentication):
|
|
|
27
28
|
Valida o token no banco de dados compartilhado
|
|
28
29
|
"""
|
|
29
30
|
try:
|
|
30
|
-
token = (
|
|
31
|
-
SharedToken.objects.using("auth_db").select_related("user").get(key=key)
|
|
32
|
-
)
|
|
31
|
+
token = SharedToken.objects.select_related("user").get(key=key)
|
|
33
32
|
except SharedToken.DoesNotExist:
|
|
34
33
|
raise exceptions.AuthenticationFailed(_("Token inválido."))
|
|
35
34
|
|
|
36
35
|
# Buscar usuário completo
|
|
37
36
|
try:
|
|
38
|
-
user = SharedUser.objects.
|
|
37
|
+
user = SharedUser.objects.get(pk=token.user_id)
|
|
39
38
|
except SharedUser.DoesNotExist:
|
|
40
39
|
raise exceptions.AuthenticationFailed(_("Usuário não encontrado."))
|
|
41
40
|
|
shared_auth/decorators.py
CHANGED
|
@@ -29,8 +29,8 @@ def require_auth(view_func):
|
|
|
29
29
|
|
|
30
30
|
# Validar token
|
|
31
31
|
try:
|
|
32
|
-
token_obj = SharedToken.objects.
|
|
33
|
-
user = SharedUser.objects.
|
|
32
|
+
token_obj = SharedToken.objects.get(key=token)
|
|
33
|
+
user = SharedUser.objects.get(pk=token_obj.user_id)
|
|
34
34
|
|
|
35
35
|
if not user.is_active or user.deleted_at is not None:
|
|
36
36
|
return JsonResponse({"error": "Usuário inativo"}, status=401)
|
|
@@ -59,9 +59,7 @@ def require_organization(view_func):
|
|
|
59
59
|
|
|
60
60
|
# Buscar organização
|
|
61
61
|
try:
|
|
62
|
-
org = SharedOrganization.objects.
|
|
63
|
-
pk=request.organization_id
|
|
64
|
-
)
|
|
62
|
+
org = SharedOrganization.objects.get(pk=request.organization_id)
|
|
65
63
|
|
|
66
64
|
if not org.is_active():
|
|
67
65
|
return JsonResponse({"error": "Organização inativa"}, status=403)
|
shared_auth/managers.py
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Managers customizados para os models compartilhados
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from django.db import models
|
|
6
5
|
from django.contrib.auth.models import UserManager
|
|
6
|
+
from django.db import models
|
|
7
|
+
|
|
7
8
|
from .exceptions import OrganizationNotFoundError, UserNotFoundError
|
|
8
9
|
|
|
9
10
|
|
|
@@ -95,6 +96,7 @@ class OrganizationQuerySetMixin:
|
|
|
95
96
|
"""
|
|
96
97
|
objects = list(self.all())
|
|
97
98
|
from . import SharedOrganization
|
|
99
|
+
|
|
98
100
|
if not objects:
|
|
99
101
|
return objects
|
|
100
102
|
|
|
@@ -103,10 +105,7 @@ class OrganizationQuerySetMixin:
|
|
|
103
105
|
|
|
104
106
|
# Buscar todas de uma vez
|
|
105
107
|
organizations = {
|
|
106
|
-
org.pk: org
|
|
107
|
-
for org in SharedOrganization.objects.using("auth_db").filter(
|
|
108
|
-
pk__in=org_ids
|
|
109
|
-
)
|
|
108
|
+
org.pk: org for org in SharedOrganization.objects.filter(pk__in=org_ids)
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
# Cachear nos objetos
|
|
@@ -132,6 +131,7 @@ class UserQuerySetMixin:
|
|
|
132
131
|
Pré-carrega dados de usuários (evita N+1)
|
|
133
132
|
"""
|
|
134
133
|
from . import SharedUser
|
|
134
|
+
|
|
135
135
|
objects = list(self.all())
|
|
136
136
|
|
|
137
137
|
if not objects:
|
|
@@ -139,10 +139,7 @@ class UserQuerySetMixin:
|
|
|
139
139
|
|
|
140
140
|
user_ids = set(obj.user_id for obj in objects)
|
|
141
141
|
|
|
142
|
-
users = {
|
|
143
|
-
user.pk: user
|
|
144
|
-
for user in SharedUser.objects.using("auth_db").filter(pk__in=user_ids)
|
|
145
|
-
}
|
|
142
|
+
users = {user.pk: user for user in SharedUser.objects.filter(pk__in=user_ids)}
|
|
146
143
|
|
|
147
144
|
for obj in objects:
|
|
148
145
|
obj._cached_user = users.get(obj.user_id)
|
|
@@ -158,6 +155,7 @@ class OrganizationUserQuerySetMixin(OrganizationQuerySetMixin, UserQuerySetMixin
|
|
|
158
155
|
Pré-carrega dados de organizações E usuários (evita N+1)
|
|
159
156
|
"""
|
|
160
157
|
from . import SharedOrganization, SharedUser
|
|
158
|
+
|
|
161
159
|
objects = list(self.all())
|
|
162
160
|
|
|
163
161
|
if not objects:
|
|
@@ -169,16 +167,10 @@ class OrganizationUserQuerySetMixin(OrganizationQuerySetMixin, UserQuerySetMixin
|
|
|
169
167
|
|
|
170
168
|
# Buscar em batch
|
|
171
169
|
organizations = {
|
|
172
|
-
org.pk: org
|
|
173
|
-
for org in SharedOrganization.objects.using("auth_db").filter(
|
|
174
|
-
pk__in=org_ids
|
|
175
|
-
)
|
|
170
|
+
org.pk: org for org in SharedOrganization.objects.filter(pk__in=org_ids)
|
|
176
171
|
}
|
|
177
172
|
|
|
178
|
-
users = {
|
|
179
|
-
user.pk: user
|
|
180
|
-
for user in SharedUser.objects.using("auth_db").filter(pk__in=user_ids)
|
|
181
|
-
}
|
|
173
|
+
users = {user.pk: user for user in SharedUser.objects.filter(pk__in=user_ids)}
|
|
182
174
|
|
|
183
175
|
# Cachear
|
|
184
176
|
for obj in objects:
|
|
@@ -191,16 +183,15 @@ class OrganizationUserQuerySetMixin(OrganizationQuerySetMixin, UserQuerySetMixin
|
|
|
191
183
|
"""
|
|
192
184
|
Cria objeto com validação de organização e usuário
|
|
193
185
|
"""
|
|
194
|
-
from . import
|
|
186
|
+
from . import SharedMember, SharedOrganization
|
|
187
|
+
|
|
195
188
|
# Valida organização
|
|
196
|
-
SharedOrganization.objects.
|
|
189
|
+
SharedOrganization.objects.get_or_fail(organization_id)
|
|
197
190
|
|
|
198
191
|
# Valida usuário pertence à organização
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
.exists()
|
|
203
|
-
):
|
|
192
|
+
if not SharedMember.objects.filter(
|
|
193
|
+
user_id=user_id, organization_id=organization_id
|
|
194
|
+
).exists():
|
|
204
195
|
raise ValueError(
|
|
205
196
|
f"Usuário {user_id} não pertence à organização {organization_id}"
|
|
206
197
|
)
|
shared_auth/middleware.py
CHANGED
|
@@ -51,8 +51,8 @@ class SharedAuthMiddleware(MiddlewareMixin):
|
|
|
51
51
|
|
|
52
52
|
# Validar token e buscar usuário
|
|
53
53
|
try:
|
|
54
|
-
token_obj = SharedToken.objects.
|
|
55
|
-
user = SharedUser.objects.
|
|
54
|
+
token_obj = SharedToken.objects.get(key=token)
|
|
55
|
+
user = SharedUser.objects.get(pk=token_obj.user_id)
|
|
56
56
|
|
|
57
57
|
if not user.is_active or user.deleted_at is not None:
|
|
58
58
|
# request.user = None
|
|
@@ -159,11 +159,9 @@ class OrganizationMiddleware(MiddlewareMixin):
|
|
|
159
159
|
return
|
|
160
160
|
|
|
161
161
|
request.organization_id = organization_id
|
|
162
|
-
request.organization = (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
.first()
|
|
166
|
-
)
|
|
162
|
+
request.organization = SharedOrganization.objects.filter(
|
|
163
|
+
pk=organization_id
|
|
164
|
+
).first()
|
|
167
165
|
|
|
168
166
|
@staticmethod
|
|
169
167
|
def _authenticate_user(request):
|
|
@@ -196,11 +194,7 @@ class OrganizationMiddleware(MiddlewareMixin):
|
|
|
196
194
|
return None
|
|
197
195
|
|
|
198
196
|
# Buscar a primeira organização que o usuário pertence
|
|
199
|
-
member = (
|
|
200
|
-
SharedMember.objects.using("auth_db")
|
|
201
|
-
.filter(user_id=request.user.pk)
|
|
202
|
-
.first()
|
|
203
|
-
)
|
|
197
|
+
member = SharedMember.objects.filter(user_id=request.user.pk).first()
|
|
204
198
|
|
|
205
199
|
return member.organization_id if member else None
|
|
206
200
|
|
shared_auth/mixins.py
CHANGED
|
@@ -3,7 +3,7 @@ Mixins para facilitar a criação de models com referências ao sistema de auth
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from django.db import models
|
|
6
|
-
from rest_framework import
|
|
6
|
+
from rest_framework import status, viewsets
|
|
7
7
|
from rest_framework.response import Response
|
|
8
8
|
|
|
9
9
|
from shared_auth.managers import BaseAuthManager
|
|
@@ -31,6 +31,7 @@ class OrganizationMixin(models.Model):
|
|
|
31
31
|
db_index=True, help_text="ID da organização no sistema de autenticação"
|
|
32
32
|
)
|
|
33
33
|
objects = BaseAuthManager()
|
|
34
|
+
|
|
34
35
|
class Meta:
|
|
35
36
|
abstract = True
|
|
36
37
|
indexes = [
|
|
@@ -45,9 +46,9 @@ class OrganizationMixin(models.Model):
|
|
|
45
46
|
if not hasattr(self, "_cached_organization"):
|
|
46
47
|
from shared_auth.models import SharedOrganization
|
|
47
48
|
|
|
48
|
-
self._cached_organization = SharedOrganization.objects.
|
|
49
|
-
|
|
50
|
-
)
|
|
49
|
+
self._cached_organization = SharedOrganization.objects.get_or_fail(
|
|
50
|
+
self.organization_id
|
|
51
|
+
)
|
|
51
52
|
return self._cached_organization
|
|
52
53
|
|
|
53
54
|
@property
|
|
@@ -94,6 +95,7 @@ class UserMixin(models.Model):
|
|
|
94
95
|
db_index=True, help_text="ID do usuário no sistema de autenticação"
|
|
95
96
|
)
|
|
96
97
|
objects = BaseAuthManager()
|
|
98
|
+
|
|
97
99
|
class Meta:
|
|
98
100
|
abstract = True
|
|
99
101
|
indexes = [
|
|
@@ -108,9 +110,7 @@ class UserMixin(models.Model):
|
|
|
108
110
|
if not hasattr(self, "_cached_user"):
|
|
109
111
|
from shared_auth.models import SharedUser
|
|
110
112
|
|
|
111
|
-
self._cached_user = SharedUser.objects.
|
|
112
|
-
self.user_id
|
|
113
|
-
)
|
|
113
|
+
self._cached_user = SharedUser.objects.get_or_fail(self.user_id)
|
|
114
114
|
return self._cached_user
|
|
115
115
|
|
|
116
116
|
@property
|
|
@@ -173,11 +173,9 @@ class OrganizationUserMixin(OrganizationMixin, UserMixin):
|
|
|
173
173
|
"""
|
|
174
174
|
from shared_auth.models import SharedMember
|
|
175
175
|
|
|
176
|
-
return (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
.exists()
|
|
180
|
-
)
|
|
176
|
+
return SharedMember.objects.filter(
|
|
177
|
+
user_id=self.user_id, organization_id=self.organization_id
|
|
178
|
+
).exists()
|
|
181
179
|
|
|
182
180
|
def user_can_access(self, user_id):
|
|
183
181
|
"""
|
|
@@ -186,11 +184,10 @@ class OrganizationUserMixin(OrganizationMixin, UserMixin):
|
|
|
186
184
|
"""
|
|
187
185
|
from shared_auth.models import SharedMember
|
|
188
186
|
|
|
189
|
-
return (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
)
|
|
187
|
+
return SharedMember.objects.filter(
|
|
188
|
+
user_id=user_id, organization_id=self.organization_id
|
|
189
|
+
).exists()
|
|
190
|
+
|
|
194
191
|
|
|
195
192
|
class LoggedOrganizationMixin(viewsets.ModelViewSet):
|
|
196
193
|
"""
|
|
@@ -249,6 +246,7 @@ class LoggedOrganizationMixin(viewsets.ModelViewSet):
|
|
|
249
246
|
else:
|
|
250
247
|
serializer.save()
|
|
251
248
|
|
|
249
|
+
|
|
252
250
|
class TimestampedMixin(models.Model):
|
|
253
251
|
"""
|
|
254
252
|
Mixin para adicionar timestamps
|
shared_auth/models.py
CHANGED
|
@@ -29,7 +29,6 @@ class SharedToken(models.Model):
|
|
|
29
29
|
class Meta:
|
|
30
30
|
managed = False
|
|
31
31
|
db_table = "authtoken_token"
|
|
32
|
-
app_label = "shared_auth"
|
|
33
32
|
|
|
34
33
|
def __str__(self):
|
|
35
34
|
return self.key
|
|
@@ -38,9 +37,7 @@ class SharedToken(models.Model):
|
|
|
38
37
|
def user(self):
|
|
39
38
|
"""Acessa usuário do token"""
|
|
40
39
|
if not hasattr(self, "_cached_user"):
|
|
41
|
-
self._cached_user = SharedUser.objects.
|
|
42
|
-
self.user_id
|
|
43
|
-
)
|
|
40
|
+
self._cached_user = SharedUser.objects.get_or_fail(self.user_id)
|
|
44
41
|
return self._cached_user
|
|
45
42
|
|
|
46
43
|
def is_valid(self):
|
|
@@ -77,7 +74,6 @@ class SharedOrganization(models.Model):
|
|
|
77
74
|
class Meta:
|
|
78
75
|
managed = False # CRITICAL: Não gera migrations
|
|
79
76
|
db_table = ORGANIZATION_TABLE
|
|
80
|
-
app_label = "shared_auth"
|
|
81
77
|
|
|
82
78
|
def __str__(self):
|
|
83
79
|
return self.fantasy_name or self.name or f"Org #{self.pk}"
|
|
@@ -152,7 +148,6 @@ class SharedUser(AbstractUser):
|
|
|
152
148
|
class Meta:
|
|
153
149
|
managed = False
|
|
154
150
|
db_table = USER_TABLE
|
|
155
|
-
app_label = "shared_auth"
|
|
156
151
|
|
|
157
152
|
|
|
158
153
|
class SharedMember(models.Model):
|
|
@@ -170,7 +165,6 @@ class SharedMember(models.Model):
|
|
|
170
165
|
class Meta:
|
|
171
166
|
managed = False
|
|
172
167
|
db_table = MEMBER_TABLE
|
|
173
|
-
app_label = "shared_auth"
|
|
174
168
|
|
|
175
169
|
def __str__(self):
|
|
176
170
|
return f"Member: User {self.user_id} - Org {self.organization_id}"
|
shared_auth/permissions.py
CHANGED
|
@@ -38,9 +38,7 @@ class HasActiveOrganization(permissions.BasePermission):
|
|
|
38
38
|
from .models import SharedOrganization
|
|
39
39
|
|
|
40
40
|
try:
|
|
41
|
-
org = SharedOrganization.objects.
|
|
42
|
-
pk=request.organization_id
|
|
43
|
-
)
|
|
41
|
+
org = SharedOrganization.objects.get(pk=request.organization_id)
|
|
44
42
|
return org.is_active()
|
|
45
43
|
except SharedOrganization.DoesNotExist:
|
|
46
44
|
return False
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
shared_auth/__init__.py,sha256=Ta8lIbyn22J6wU-TgWeSvdDHS4NBRf4Sv1Ag8VtY-bE,152
|
|
2
|
-
shared_auth/app.py,sha256=EHoLKlpW41o6ZxcH184aMhnWQxkVp9fH2_-89RjMz-4,215
|
|
3
|
-
shared_auth/authentication.py,sha256=btrTjBszPrKKfKA4F0pOOzaxnfASgXb1y2cxQwOWbnk,1515
|
|
4
|
-
shared_auth/conf.py,sha256=-jdnCokMvWvVKllfsxNYCsPk1Vo3MDRq4Y1MsO16oeA,411
|
|
5
|
-
shared_auth/decorators.py,sha256=k1GurkcJUL_WKK1wNEfFNfjo5Td_S7v5YI_3E-zi9kc,3336
|
|
6
|
-
shared_auth/exceptions.py,sha256=eiII-REupK6GeFinisteYO3FsGUDAN5zAajXPhTREm8,404
|
|
7
|
-
shared_auth/fields.py,sha256=RAcmFh1D_nkbai_7t_OrPZhfhAipesy5kKnEj4LUvvM,1254
|
|
8
|
-
shared_auth/managers.py,sha256=ykfa11rMR3axuc_Kl_ab_SJ_R6pufwJS2h3SVr318-w,7089
|
|
9
|
-
shared_auth/middleware.py,sha256=4IUr5qWEpJfhwCkUTxv8jkcltasn2ZFuCWL93qrK6jI,6270
|
|
10
|
-
shared_auth/mixins.py,sha256=3ARghQ4TvtENKoA5UTOHTrRHa6ufMRVJJHGFkV8-BL8,7550
|
|
11
|
-
shared_auth/models.py,sha256=tMT84SA99V7lt-nfvP73fklN6pAidWxgHO04NgEZh3M,5342
|
|
12
|
-
shared_auth/permissions.py,sha256=t1YVLBKnpAo4pmSNvqC9mW08CdOE99q2fpf-Heuo_Bs,2712
|
|
13
|
-
shared_auth/router.py,sha256=zYidJ7j40lQLrhkCtAQAp-rQLhua_UF0X7SDzYRcV5w,668
|
|
14
|
-
shared_auth/serializers.py,sha256=TDpuZVsOL-6igINSOOOyELWbTUeet4XWRoBkvcMGjW4,4290
|
|
15
|
-
maquinaweb_shared_auth-0.2.0.dist-info/METADATA,sha256=QexSdWCIE_jf-vqf1WWOkLmGwI1vDC991imGnvUNQi4,27150
|
|
16
|
-
maquinaweb_shared_auth-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
-
maquinaweb_shared_auth-0.2.0.dist-info/top_level.txt,sha256=msyYRy02ZV7zz7GR1raUI5LXGFIFn2TIkgkeKZqKufE,12
|
|
18
|
-
maquinaweb_shared_auth-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
{maquinaweb_shared_auth-0.2.0.dist-info → maquinaweb_shared_auth-0.2.2.dist-info}/top_level.txt
RENAMED
|
File without changes
|