maquinaweb-shared-auth 0.2.13__py3-none-any.whl → 0.2.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.

Potentially problematic release.


This version of maquinaweb-shared-auth might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maquinaweb-shared-auth
3
- Version: 0.2.13
3
+ Version: 0.2.15
4
4
  Summary: Models read-only para autenticação compartilhada entre projetos Django.
5
5
  Author-email: Seu Nome <seuemail@dominio.com>
6
6
  License: MIT
@@ -0,0 +1,20 @@
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=2dcmq2ATYxvbPXgXd2duNNfiSmwIubg_5yrYa90STsI,1432
4
+ shared_auth/conf.py,sha256=-jdnCokMvWvVKllfsxNYCsPk1Vo3MDRq4Y1MsO16oeA,411
5
+ shared_auth/decorators.py,sha256=RT-Qfi7oGBo6PvWJRR1dqJUQdU6ZOf9p-8mV3rZmqQ0,3237
6
+ shared_auth/exceptions.py,sha256=VKamHjBl2yjXG2RsMrLrXru1_Q9IJXmy4xmDcXlpWsU,627
7
+ shared_auth/fields.py,sha256=RAcmFh1D_nkbai_7t_OrPZhfhAipesy5kKnEj4LUvvM,1254
8
+ shared_auth/managers.py,sha256=IF12LmQslupcQhpazmxksrWBhy3N4bZrgU8hP6bHqII,6781
9
+ shared_auth/middleware.py,sha256=8pi5UEj3TLLrjrVBp2Jh8F015urncUSewJTjTj5UsY8,6106
10
+ shared_auth/mixins.py,sha256=BSYNTWYjLRGuxfy7x-uAaNZmrTMsm67ogAQzjrU2DoQ,7388
11
+ shared_auth/models.py,sha256=PHVLpJUk3gzmiQYHPkk_G1KyUo8qXGolCioK8vg2xu0,6201
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=uhSrcmFgBUnIWUiKymQrsSNpFDTj3j1Eged2lPG2M0s,4588
15
+ shared_auth/urls.py,sha256=Bj3EtpLDyNBEgtfVN0gVf11STOPJV8hzAnOb_cF2xi4,298
16
+ shared_auth/views.py,sha256=r4UU97TDvhJdqPr4ifIYSflRaUBeQ9DMaDAxby7l3k0,1572
17
+ maquinaweb_shared_auth-0.2.15.dist-info/METADATA,sha256=00iVBSYhh26aw4TA_z8yMRgwKmglsq5JIXM6o1z5pio,27151
18
+ maquinaweb_shared_auth-0.2.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ maquinaweb_shared_auth-0.2.15.dist-info/top_level.txt,sha256=msyYRy02ZV7zz7GR1raUI5LXGFIFn2TIkgkeKZqKufE,12
20
+ maquinaweb_shared_auth-0.2.15.dist-info/RECORD,,
@@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _
6
6
  from rest_framework import exceptions
7
7
  from rest_framework.authentication import TokenAuthentication
8
8
 
9
- from .models import SharedToken, SharedUser
9
+ from .models import SharedToken, User
10
10
 
11
11
 
12
12
  class SharedTokenAuthentication(TokenAuthentication):
@@ -34,8 +34,8 @@ class SharedTokenAuthentication(TokenAuthentication):
34
34
 
35
35
  # Buscar usuário completo
36
36
  try:
37
- user = SharedUser.objects.get(pk=token.user_id)
38
- except SharedUser.DoesNotExist:
37
+ user = User.objects.get(pk=token.user_id)
38
+ except User.DoesNotExist:
39
39
  raise exceptions.AuthenticationFailed(_("Usuário não encontrado."))
40
40
 
41
41
  if not user.is_active:
shared_auth/decorators.py CHANGED
@@ -6,7 +6,7 @@ from functools import wraps
6
6
 
7
7
  from django.http import JsonResponse
8
8
 
9
- from .models import SharedOrganization, SharedToken, SharedUser
9
+ from .models import SharedOrganization, SharedToken, User
10
10
 
11
11
 
12
12
  def require_auth(view_func):
@@ -30,7 +30,7 @@ def require_auth(view_func):
30
30
  # Validar token
31
31
  try:
32
32
  token_obj = SharedToken.objects.get(key=token)
33
- user = SharedUser.objects.get(pk=token_obj.user_id)
33
+ user = User.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)
@@ -38,7 +38,7 @@ def require_auth(view_func):
38
38
  request.user = user
39
39
  request.auth = token_obj
40
40
 
41
- except (SharedToken.DoesNotExist, SharedUser.DoesNotExist):
41
+ except (SharedToken.DoesNotExist, User.DoesNotExist):
42
42
  return JsonResponse({"error": "Token inválido"}, status=401)
43
43
 
44
44
  return view_func(request, *args, **kwargs)
shared_auth/exceptions.py CHANGED
@@ -1,21 +1,23 @@
1
+ from rest_framework.exceptions import APIException
1
2
  """
2
3
  Exceções customizadas
3
4
  """
4
- class SharedAuthError(Exception):
5
+ class SharedAuthError(APIException):
5
6
  """Erro base da biblioteca"""
6
7
  pass
7
8
 
8
9
 
9
10
  class OrganizationNotFoundError(SharedAuthError):
10
- """Organização não encontrada"""
11
- pass
12
-
11
+ status_code = 404
12
+ default_detail = 'Organização não encontrada.'
13
+ default_code = 'organization_not_found'
13
14
 
14
15
  class UserNotFoundError(SharedAuthError):
15
- """Usuário não encontrado"""
16
- pass
17
-
16
+ status_code = 404
17
+ default_detail = 'Usuário não encontrado.'
18
+ default_code = 'user_not_found'
18
19
 
19
20
  class DatabaseConnectionError(SharedAuthError):
20
- """Erro de conexão com o banco de dados"""
21
- pass
21
+ status_code = 500
22
+ default_detail = 'Erro interno'
23
+ default_code = 'internal_error'
shared_auth/managers.py CHANGED
@@ -220,6 +220,3 @@ class BaseAuthManager(models.Manager):
220
220
  return super().get_queryset()
221
221
 
222
222
  return qs_class(self.model, using=self._db)
223
-
224
-
225
- # shared_auth/serializers.py
shared_auth/middleware.py CHANGED
@@ -6,7 +6,7 @@ from django.http import JsonResponse
6
6
  from django.utils.deprecation import MiddlewareMixin
7
7
 
8
8
  from .authentication import SharedTokenAuthentication
9
- from .models import SharedMember, SharedOrganization, SharedToken, SharedUser
9
+ from .models import SharedMember, SharedOrganization, SharedToken, User
10
10
 
11
11
 
12
12
  class SharedAuthMiddleware(MiddlewareMixin):
@@ -52,7 +52,7 @@ class SharedAuthMiddleware(MiddlewareMixin):
52
52
  # Validar token e buscar usuário
53
53
  try:
54
54
  token_obj = SharedToken.objects.get(key=token)
55
- user = SharedUser.objects.get(pk=token_obj.user_id)
55
+ user = User.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
@@ -63,7 +63,7 @@ class SharedAuthMiddleware(MiddlewareMixin):
63
63
  # request.user = user
64
64
  request.auth = token_obj
65
65
 
66
- except (SharedToken.DoesNotExist, SharedUser.DoesNotExist):
66
+ except (SharedToken.DoesNotExist, User.DoesNotExist):
67
67
  # request.user = None
68
68
  request.auth = None
69
69
 
shared_auth/mixins.py CHANGED
@@ -108,9 +108,9 @@ class UserMixin(models.Model):
108
108
  Acessa usuário do banco de auth (lazy loading com cache)
109
109
  """
110
110
  if not hasattr(self, "_cached_user"):
111
- from shared_auth.models import SharedUser
111
+ from shared_auth.models import User
112
112
 
113
- self._cached_user = SharedUser.objects.get_or_fail(self.user_id)
113
+ self._cached_user = User.objects.get_or_fail(self.user_id)
114
114
  return self._cached_user
115
115
 
116
116
  @property
shared_auth/models.py CHANGED
@@ -5,7 +5,7 @@ ATENÇÃO: Estes models NÃO devem ser usados para criar migrations
5
5
 
6
6
  from django.contrib.auth.models import AbstractUser
7
7
  from django.db import models
8
-
8
+ from .exceptions import OrganizationNotFoundError, PermissionDeniedError
9
9
  from .conf import MEMBER_TABLE, ORGANIZATION_TABLE, USER_TABLE
10
10
  from .managers import (
11
11
  SharedMemberManager,
@@ -37,7 +37,7 @@ class SharedToken(models.Model):
37
37
  def user(self):
38
38
  """Acessa usuário do token"""
39
39
  if not hasattr(self, "_cached_user"):
40
- self._cached_user = SharedUser.objects.get_or_fail(self.user_id)
40
+ self._cached_user = User.objects.get_or_fail(self.user_id)
41
41
  return self._cached_user
42
42
 
43
43
  def is_valid(self):
@@ -121,7 +121,7 @@ class SharedOrganization(models.Model):
121
121
  Usage:
122
122
  users = org.users
123
123
  """
124
- return SharedUser.objects.filter(
124
+ return User.objects.filter(
125
125
  id__in=self.members.values_list("user_id", flat=True)
126
126
  )
127
127
 
@@ -130,7 +130,7 @@ class SharedOrganization(models.Model):
130
130
  return self.deleted_at is None
131
131
 
132
132
 
133
- class SharedUser(AbstractUser):
133
+ class User(AbstractUser):
134
134
  """
135
135
  Model READ-ONLY da tabela auth_user
136
136
  """
@@ -148,10 +148,36 @@ class SharedUser(AbstractUser):
148
148
  class Meta:
149
149
  managed = False
150
150
  db_table = USER_TABLE
151
- default_related_name = "user"
152
- name = "user"
153
- verbose_name = "user"
154
- verbose_name_plural = "users"
151
+
152
+ @property
153
+ def organizations(self):
154
+ """
155
+ Retorna todas as organizações associadas ao usuário.
156
+ """
157
+ return SharedOrganization.objects.filter(
158
+ id__in=SharedMember.objects.filter(user_id=self.id).values_list(
159
+ "organization_id", flat=True
160
+ )
161
+ )
162
+
163
+ def get_org(self, organization_id):
164
+ """
165
+ Retorna a organização especificada pelo ID, se o usuário for membro.
166
+ """
167
+ try:
168
+ organization = SharedOrganization.objects.get(id=organization_id)
169
+ except SharedOrganization.DoesNotExist:
170
+ raise OrganizationNotFoundError(
171
+ f"Organização com ID {organization_id} não encontrada."
172
+ )
173
+
174
+ if not SharedMember.objects.filter(
175
+ user_id=self.id, organization_id=organization.id
176
+ ).exists():
177
+ raise OrganizationNotFoundError("Usuário não é membro desta organização.")
178
+
179
+ return organization
180
+
155
181
 
156
182
 
157
183
  class SharedMember(models.Model):
@@ -183,7 +209,7 @@ class SharedMember(models.Model):
183
209
  user = member.user
184
210
  print(user.email)
185
211
  """
186
- return SharedUser.objects.get_or_fail(self.user_id)
212
+ return User.objects.get_or_fail(self.user_id)
187
213
 
188
214
  @property
189
215
  def organization(self):
@@ -1,8 +1,8 @@
1
1
  """
2
2
  Serializers compartilhados para DRF
3
3
  """
4
-
5
4
  from rest_framework import serializers
5
+ from .models import SharedOrganization, User
6
6
 
7
7
 
8
8
  class OrganizationSerializerMixin:
@@ -49,6 +49,16 @@ class OrganizationSerializerMixin:
49
49
  except Exception as e:
50
50
  return None
51
51
 
52
+ class OrganizationSerializer(serializers.ModelSerializer):
53
+ class Meta:
54
+ model = SharedOrganization
55
+ fields = "__all__"
56
+
57
+
58
+ class UserSerializer(serializers.ModelSerializer):
59
+ class Meta:
60
+ model = User
61
+ fields = "__all__"
52
62
 
53
63
  class UserSerializerMixin:
54
64
  """
shared_auth/urls.py ADDED
@@ -0,0 +1,8 @@
1
+ from rest_framework.routers import DefaultRouter
2
+ from .views import OrganizationViewSet, UserViewSet
3
+
4
+ router = DefaultRouter()
5
+ router.register(r'api/organizations', OrganizationViewSet, basename='organization')
6
+ router.register(r'auth/user', UserViewSet, basename='user')
7
+
8
+ urlpatterns = router.urls
shared_auth/views.py ADDED
@@ -0,0 +1,41 @@
1
+ from rest_framework import viewsets
2
+ from rest_framework.decorators import action
3
+ from rest_framework.response import Response
4
+ from rest_framework.permissions import IsAuthenticated
5
+ from .middleware import get_member
6
+ from .models import SharedOrganization, SharedMember
7
+ from .serializers import OrganizationSerializer, UserSerializer
8
+
9
+ class OrganizationViewSet(viewsets.ReadOnlyModelViewSet):
10
+ """
11
+ ViewSet para organizações do usuário + action `me` para retornar
12
+ a organização atual via header.
13
+ """
14
+ serializer_class = OrganizationSerializer
15
+ permission_classes = [IsAuthenticated]
16
+
17
+ def get_queryset(self):
18
+ organizations = self.request.user.organizations
19
+ return organizations
20
+
21
+ @action(detail=False, methods=['get'])
22
+ def me(self, request):
23
+ org = request.user.get_org(request.organization_id)
24
+ if not org:
25
+ return Response({"detail": "Organization not specified or not found."}, status=400)
26
+
27
+ if not get_member(request.user.id, org.pk):
28
+ return Response({"detail": "Você não pertence a essa organização."}, status=403)
29
+ serializer = self.get_serializer(org)
30
+ return Response(serializer.data)
31
+
32
+ class UserViewSet(viewsets.ReadOnlyModelViewSet):
33
+ serializer_class = UserSerializer
34
+ permission_classes = [IsAuthenticated]
35
+
36
+ # def get_queryset(self):
37
+ # return User.objects.filter(pk=self.request.user.pk)
38
+
39
+ def list(self, request, *args, **kwargs):
40
+ serializer = self.get_serializer(request.user)
41
+ return Response(serializer.data)
@@ -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=-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=HB5jCqa7l-P1o6v_Gk043RRjzv87xAXpYQ7jDlz7NZg,5287
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.13.dist-info/METADATA,sha256=YijgfRMLll6LeiRmcMBnjYyFKzODMbXUX39BtfJa8l4,27151
16
- maquinaweb_shared_auth-0.2.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- maquinaweb_shared_auth-0.2.13.dist-info/top_level.txt,sha256=msyYRy02ZV7zz7GR1raUI5LXGFIFn2TIkgkeKZqKufE,12
18
- maquinaweb_shared_auth-0.2.13.dist-info/RECORD,,