From 61cf354a796bfdccd07be2eaae063b386dd7e77e Mon Sep 17 00:00:00 2001 From: fjrodriguez Date: Mon, 13 Nov 2023 11:45:10 -0600 Subject: [PATCH] Added Cambios Permisos y Modulos --- Clientes/views.py | 2 + IMMEX/admin.py | 157 ++++++++- IMMEX/migrations/0003_activetokensession.py | 26 ++ .../0004_modulo_permisos_a24_and_more.py | 62 ++++ .../0005_suspensionpermiso_suspendido.py | 18 ++ .../0006_remove_modulo_sistema_cliente.py | 17 + IMMEX/models.py | 57 +++- IMMEX/permissions.py | 111 +++++++ IMMEX/serializers.py | 34 +- IMMEX/urls.py | 13 +- IMMEX/views.py | 303 ++++++++++++++++-- Sistemas/admin.py | 4 +- Sistemas/permissions.py | 17 +- 13 files changed, 772 insertions(+), 49 deletions(-) create mode 100644 IMMEX/migrations/0003_activetokensession.py create mode 100644 IMMEX/migrations/0004_modulo_permisos_a24_and_more.py create mode 100644 IMMEX/migrations/0005_suspensionpermiso_suspendido.py create mode 100644 IMMEX/migrations/0006_remove_modulo_sistema_cliente.py create mode 100644 IMMEX/permissions.py diff --git a/Clientes/views.py b/Clientes/views.py index a1d5b51..d5e80dc 100644 --- a/Clientes/views.py +++ b/Clientes/views.py @@ -439,6 +439,8 @@ class CancelaTimbre(APIView): BitacoraErrores.objects.create(level=2, message=msn, traceback=traceback.format_exc(), view='Sistemas.CancelaTimbre') return Response({'Error':f'{ex}','isError':True}) + + class add_timbre(APIView): pass diff --git a/IMMEX/admin.py b/IMMEX/admin.py index bfbfcb1..07ca77e 100644 --- a/IMMEX/admin.py +++ b/IMMEX/admin.py @@ -1,9 +1,164 @@ +from typing import Any from django.contrib import admin -from . models import Sistemas_por_cliente_A24, ClientesA24, DeviceA24 +from .models import * +from django.contrib.sessions.models import Session +from django.contrib.auth.models import User +from django.utils import timezone +from datetime import timedelta +from django.db.models import Q +class SessionAdmin(admin.ModelAdmin): + list_display=['get_username','session_key','session_data','expire_date'] + readonly_fields = ['display_session_data', 'get_username'] + + def display_session_data(self,obj): + session_data =obj.get_decoded() + formatted_data = "\n".join([f"{key}: {value}" for key, value in session_data.items()]) + return formatted_data + display_session_data.shoort_description= "Session Data" + + def get_username(self,obj): + session_data=obj.get_decoded() + user_id=session_data.get('_auth_user_id') + try: + user=User.objects.get(id=user_id) + return user.username + except User.DoesNotExist: + return "Usuario inexistente" + get_username.short_description = 'Nombre Usuario' + + def delete_expired_sessions(self,request,queryset): + now =timezone.now() + one_day_ago = now- timedelta(days=1) + + expired_sessions = queryset.filter(expire_date__lte=one_day_ago) + expired_sessions.delete() + delete_expired_sessions.short_description = 'Eliminar Sessiones caducas' + + def get_queryset(self,request): + now =timezone.now() + one_day_ago = now- timedelta(days=1) + current_session = request.session.session_key + + queryset = super().get_queryset(request)#.filter(expire_date__gte=one_day_ago) + #queryset.exclude(session_key=current_session) + + return queryset + + + class Device_A24_Admin(admin.ModelAdmin): list_display = ['clienteA24', 'dataBase','username','sistema','MAC','deviceName'] + +class ActiveTokenSession_Admin(admin.ModelAdmin): + list_display = ['start_time', 'last_time', 'has_expired'] + +class SuspensionPermisoInline(admin.TabularInline): + model = SuspensionPermiso + extra = 1 + def get_formset(self, request, obj=None, **kwargs): + # Utiliza el objeto 'obj' (el módulo actual) para filtrar el queryset + formset = super().get_formset(request, obj, **kwargs) + + if obj: + print('SDL',obj.modulo.permisos.all()) + formset.form.base_fields['permiso'].queryset = obj.modulo.permisos.all() + return formset + +class SuspensionModuloInline(admin.TabularInline): + model = SuspensionModulo + + +class SuspensionModuloAdmin(admin.ModelAdmin): + list_display = ['modulo','suspendido','supension_modPerm_id'] + inlines=[SuspensionPermisoInline] + + +class Suspension_Modulos_Permisos_ClienteAdmin(admin.ModelAdmin): + inlines = [SuspensionModuloInline] + + def save_model(self, request, obj, form, change): + super().save_model(request, obj, form, change) + + for modulo in obj.suspensionmodulo_set.all(): + for permiso in modulo.modulo.permisos.all(): + permObj,created=SuspensionPermiso.objects.get_or_create( + suspension_modulo=modulo, + permiso=permiso, + ) + + + + + +admin.site.register(Permisos_A24) +admin.site.register(Modulo) +admin.site.register(SuspensionModulo,SuspensionModuloAdmin) +admin.site.register(Suspension_Modulos_Permisos_Cliente, Suspension_Modulos_Permisos_ClienteAdmin) +# class Permisos_A24_Admin(admin.ModelAdmin): +# list_display =['nombre'] +# search_fields = ['nombre'] + +# class Modulo_Admin(admin.ModelAdmin): +# list_display=('nombre','sistema_cliente' +# #,'lista_permisos' +# ) +# filter_horizontal = ['permisos'] + + +# class SuspensionPermisoInline(admin.TabularInline): +# model = SuspensionPermiso +# extra = 1 # Puedes ajustar esto según tus necesidades + +# class SuspensionModuloInline(admin.TabularInline): +# model = SuspensionModulo +# extra = 1 # Ajusta según tus necesidades + +# class SuspensionModulo_Admin(admin.ModelAdmin): +# list_display=['modulo','cliente_nombre','dispositivo','custom_suspendido'] +# search_fields = ['dispositivo__clienteA24__Nombre', 'dispositivo__clienteA24__RFC'] + +# def cliente_nombre(self, obj): +# return f'{obj.dispositivo.clienteA24.Nombre}({obj.dispositivo.clienteA24.RFC})' \ +# if obj.dispositivo and obj.dispositivo.clienteA24 else "N/A" +# cliente_nombre.short_description = 'Nombre del Cliente(RFC)' # Puedes personalizar el encabezado si lo deseas + +# def custom_suspendido(self,obj): +# return "Sí" if obj.suspendido else "No" + +# custom_suspendido.short_description = 'Suspendido' + + +# class ModuloInline(admin.TabularInline): +# model = Modulo +# extra =1 +# autocomplete_fields = ['permisos'] + +# class Sistema_x_ClienteAdmin(admin.ModelAdmin): +# inlines = [ModuloInline] +# search_fields = ['modulos__nombre', 'modulos__permisos__nombre'] +# list_filter =('modulo__permisos__nombre',) + +# class Device_A24_Admin2(admin.ModelAdmin): +# inlines = [SuspensionPermisoInline, SuspensionModuloInline] + + + +# admin.site.register(SuspensionPermiso) +# admin.site.register(SuspensionModulo,SuspensionModulo_Admin) + +# admin.site.register(Permisos_A24,Permisos_A24_Admin) +# admin.site.register(Modulo,Modulo_Admin) + + admin.site.register(Sistemas_por_cliente_A24) + admin.site.register(ClientesA24) admin.site.register(DeviceA24, Device_A24_Admin) + +admin.site.register(ActiveTokenSession, ActiveTokenSession_Admin) +admin.site.register(Session, SessionAdmin) + + + diff --git a/IMMEX/migrations/0003_activetokensession.py b/IMMEX/migrations/0003_activetokensession.py new file mode 100644 index 0000000..905e6a1 --- /dev/null +++ b/IMMEX/migrations/0003_activetokensession.py @@ -0,0 +1,26 @@ +# Generated by Django 4.1.3 on 2023-10-18 21:11 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('authtoken', '0003_tokenproxy'), + ('IMMEX', '0002_devicea24'), + ] + + operations = [ + migrations.CreateModel( + name='ActiveTokenSession', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_time', models.DateTimeField(default=django.utils.timezone.now)), + ('last_time', models.DateTimeField(default=django.utils.timezone.now)), + ('has_expired', models.BooleanField(default=False)), + ('token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='authtoken.token')), + ], + ), + ] diff --git a/IMMEX/migrations/0004_modulo_permisos_a24_and_more.py b/IMMEX/migrations/0004_modulo_permisos_a24_and_more.py new file mode 100644 index 0000000..4401272 --- /dev/null +++ b/IMMEX/migrations/0004_modulo_permisos_a24_and_more.py @@ -0,0 +1,62 @@ +# Generated by Django 4.1.3 on 2023-10-19 14:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('IMMEX', '0003_activetokensession'), + ] + + operations = [ + migrations.CreateModel( + name='Modulo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nombre', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Permisos_A24', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nombre', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Suspension_Modulos_Permisos_Cliente', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sistema_cliente', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='IMMEX.sistemas_por_cliente_a24')), + ], + ), + migrations.CreateModel( + name='SuspensionModulo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('suspendido', models.BooleanField(default=False)), + ('modulo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='IMMEX.modulo')), + ('supension_modPerm_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='IMMEX.suspension_modulos_permisos_cliente')), + ], + ), + migrations.CreateModel( + name='SuspensionPermiso', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('permiso', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='IMMEX.permisos_a24')), + ('suspension_modulo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='IMMEX.suspensionmodulo')), + ], + ), + migrations.AddField( + model_name='modulo', + name='permisos', + field=models.ManyToManyField(to='IMMEX.permisos_a24'), + ), + migrations.AddField( + model_name='modulo', + name='sistema_cliente', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='IMMEX.sistemas_por_cliente_a24'), + ), + ] diff --git a/IMMEX/migrations/0005_suspensionpermiso_suspendido.py b/IMMEX/migrations/0005_suspensionpermiso_suspendido.py new file mode 100644 index 0000000..c6bda02 --- /dev/null +++ b/IMMEX/migrations/0005_suspensionpermiso_suspendido.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.3 on 2023-10-19 15:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('IMMEX', '0004_modulo_permisos_a24_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='suspensionpermiso', + name='suspendido', + field=models.BooleanField(default=False), + ), + ] diff --git a/IMMEX/migrations/0006_remove_modulo_sistema_cliente.py b/IMMEX/migrations/0006_remove_modulo_sistema_cliente.py new file mode 100644 index 0000000..6ac6b2b --- /dev/null +++ b/IMMEX/migrations/0006_remove_modulo_sistema_cliente.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.3 on 2023-10-19 15:43 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('IMMEX', '0005_suspensionpermiso_suspendido'), + ] + + operations = [ + migrations.RemoveField( + model_name='modulo', + name='sistema_cliente', + ), + ] diff --git a/IMMEX/models.py b/IMMEX/models.py index c5b2bd0..3a33693 100644 --- a/IMMEX/models.py +++ b/IMMEX/models.py @@ -2,7 +2,7 @@ from django.db import models from django.contrib.auth.models import User from Sistemas.models import Sistema from rest_framework.authtoken.models import Token - +from django.utils import timezone import re class ClientesA24(models.Model): @@ -21,7 +21,7 @@ class Sistemas_por_cliente_A24(models.Model): cliente = models.ForeignKey(ClientesA24, related_name='cliente_spc_IMMEX', on_delete=models.CASCADE) num_licencias= models.IntegerField(default=1) def __str__(self): - return f'{self.cliente.Nombre}' + return f'{self.cliente.Nombre}, {self.id_sistema.nombre_sistema} ' class Meta: ordering= ('-cliente','id_sistema') unique_together = ('id_sistema', 'cliente') @@ -46,7 +46,7 @@ class DeviceA24(models.Model): def generate_username(self,client, device_name, mac_address): """""" - username_ = f"SCAII_{client.RFC}_{device_name}_{mac_address}" + username_ = f"IMMEX_{client.RFC}_{device_name}_{mac_address}" username_ = re.sub(r'\W+', '', username_) if User.objects.filter(username=username_).exists(): @@ -64,4 +64,53 @@ class DeviceA24(models.Model): super().save(*args, **kwargs) def __str__(self): - return f'{self.username}' \ No newline at end of file + return f'{self.username}' + + +class ActiveTokenSession(models.Model): + token = models.ForeignKey(Token, on_delete=models.CASCADE, db_index=True) + start_time = models.DateTimeField(default=timezone.now) + last_time = models.DateTimeField(default=timezone.now) + has_expired = models.BooleanField(default=False) + + def is_expired(self, expiration_minutes): + expiration_time = self.start_time + timezone.timedelta(minutes=expiration_minutes) + return expiration_time < timezone.now() + + +##--------------------------PERMISOS +class Permisos_A24(models.Model): + nombre= models.CharField(max_length=100) + def __str__(self) -> str: + return f'{self.nombre}' + +class Modulo(models.Model): + nombre = models.CharField(max_length=100) + #sistema_cliente = models.ForeignKey(Sistemas_por_cliente_A24, on_delete=models.CASCADE) + permisos = models.ManyToManyField(Permisos_A24) + def __str__(self) -> str: + return f'{self.nombre}' + + +class Suspension_Modulos_Permisos_Cliente(models.Model): + sistema_cliente = models.ForeignKey(Sistemas_por_cliente_A24, on_delete=models.CASCADE) + def __str__(self) -> str: + return f'{self.sistema_cliente} {self.sistema_cliente.id_sistema.nombre_sistema}' +class SuspensionModulo(models.Model): + supension_modPerm_id = models.ForeignKey(Suspension_Modulos_Permisos_Cliente, on_delete=models.CASCADE) + modulo = models.ForeignKey(Modulo, on_delete=models.CASCADE) + suspendido = models.BooleanField(default=False) + def __str__(self) -> str: + return f'{self.modulo}' + + +class SuspensionPermiso(models.Model): + suspension_modulo = models.ForeignKey(SuspensionModulo, on_delete=models.CASCADE) + permiso = models.ForeignKey(Permisos_A24, on_delete=models.CASCADE) + suspendido = models.BooleanField(default=False) + + def __str__(self) -> str: + return f'{self.permiso}' + + + diff --git a/IMMEX/permissions.py b/IMMEX/permissions.py new file mode 100644 index 0000000..887196a --- /dev/null +++ b/IMMEX/permissions.py @@ -0,0 +1,111 @@ +from rest_framework.permissions import BasePermission + +from .models import ActiveTokenSession,Sistemas_por_cliente_A24,SuspensionModulo +from django.utils import timezone + +class TokenCheckSession(BasePermission): + message = 'NO' + def has_permission(self,request, view): + print('FROM TokenCheckSession : ') + sistema = request.META.get('HTTP_SISTEMA','') + mac = request.META.get('HTTP_MAC','') + RFC = request.META.get('HTTP_RFC','') + + modulo = request.META.get('HTTP_MODULO','') + permiso= request.META.get('HTTP_PERMISO','') + + active_session, created = ActiveTokenSession.objects.get_or_create(token=request.user.auth_token) + sistema_cliente = Sistemas_por_cliente_A24.objects.filter(id_sistema__nombre_sistema=sistema,cliente__RFC=RFC).first() + if sistema_cliente is None: + self.message +=f';El sistema:{sistema}, no se encuentra dado de alta para el RFC:{RFC}' + return False + + modulo_ids = sistema_cliente.suspension_modulos_permisos_cliente_set.filter(suspensionmodulo__modulo__nombre=modulo).first() + + if modulo_ids is None: + self.message +=f";El modulo:{modulo}, no se encuentra dado de alta para el RFC:{RFC} enviado" + return False + for modulo in modulo_ids.suspensionmodulo_set.filter(modulo__nombre=modulo): + if modulo.suspendido: + self.message +=f';El modulo:{modulo} se encuentra suspendido para el RFC:{RFC}' + return False + if modulo.suspensionpermiso_set.filter(permiso__nombre=permiso).first() is None: + self.message += f';El permiso:{permiso} en el modulo:{modulo} no esta dado de alta para el RFC:{RFC}' + return False + + + inner_sistema = active_session.token.devicea24_set.first().sistema.nombre_sistema == sistema + inner_mac = active_session.token.devicea24_set.first().MAC == mac + + expiro = active_session.is_expired(1) + + if not (inner_sistema and inner_mac): + self.message+=";no se agregó código de máquina y/o sistema correctamente" + if expiro: + self.message+=";la session expiro" + + if active_session.token.devicea24_set.first() is None: + self.message +=';Device not Found' + return False + #print(expiro , inner_sistema , inner_mac) + + if not (inner_sistema and inner_mac): + self.message+=";no se agregó código de máquina y/o sistema correctamente" + return False + + active_session.last_time=timezone.now() + active_session.has_expired =True if expiro else False + active_session.save() + + return False if expiro else True + + +class ActiveTokenSessionPerm(BasePermission): + message = 'NO' + def has_permission(self,request, view): + authorization = request.META.get('HTTP_AUTHORIZATION','') + sistema = request.META.get('HTTP_SISTEMA','') + mac = request.META.get('HTTP_MAC','') + if 'Token' in authorization: + + active_session, created = ActiveTokenSession.objects.get_or_create(token=request.user.auth_token) + #print(active_session.is_expired(1)) + #verifica que este el DeviceA24 dado de alta + if active_session.token.devicea24_set.first() is None: + self.message +=';Device not Found' + return False + + #print(active_session.token.devicea24_set.first().sistema.nombre_sistema) + inner_mac = active_session.token.devicea24_set.first().MAC + if mac != inner_mac: + self.message +=f';El Codigo de maquina `{mac}` proporcionado para esta licencia no es igual al registrado para este equipo `{inner_mac}`' + return False + + + + inner_sistema= active_session.token.devicea24_set.first().sistema.nombre_sistema + if sistema != inner_sistema: + self.message +=f';El Sistema `{sistema}` proporcionado para esta licencia no es igual al registrado para este equipo `{inner_sistema}`' + return False + + if created: #si el registro es recien creado la session es valida + return True + + #si la session esta expidara la renueva + print('expirooo',active_session.is_expired(1)) + if active_session.is_expired(1): + active_session.start_time = timezone.now() + active_session.last_time=timezone.now() + active_session.has_expired =False + active_session.save() + + return True + # if active_session.has_expired: + # self.message += ';La Session ha expidado' + # return False + # else: + # active_session.last_time=timezone.now() + # active_session.save() + # return True + # si no encuentra el Header[Authorization] con Token no deja entrar + return False \ No newline at end of file diff --git a/IMMEX/serializers.py b/IMMEX/serializers.py index f1ad972..25c469f 100644 --- a/IMMEX/serializers.py +++ b/IMMEX/serializers.py @@ -1,10 +1,22 @@ from rest_framework import serializers from django.contrib.auth import get_user_model from allauth.account.models import EmailAddress +from django.contrib.auth.models import Permission from Sistemas.models import Sistema -from .models import ClientesA24, Sistemas_por_cliente_A24, DeviceA24 +from .models import ClientesA24, Sistemas_por_cliente_A24, DeviceA24,Modulo +class CustomPermissionSerializer(serializers.ModelSerializer): + activo = serializers.BooleanField(read_only=True) + class Meta: + model = Permission + fields = '__all__' + +class ModulosSerializer(serializers.ModelSerializer): + class Meta: + model=Modulo + fields = '__all__' + depth =1 class Sistema_A24_PKRF(serializers.PrimaryKeyRelatedField): def to_internal_value(self,data): @@ -12,7 +24,7 @@ class Sistema_A24_PKRF(serializers.PrimaryKeyRelatedField): return Sistema.objects.get(nombre_sistema=data) except Sistema.DoesNotExist: raise serializers.ValidationError("Sistema no existe") - + class ClientA24_PKRF(serializers.PrimaryKeyRelatedField): def to_internal_value(self,data): try: @@ -39,17 +51,29 @@ class ClientesA24Serailizer(serializers.ModelSerializer): model =ClientesA24 fields = ('pk','RFC', 'Nombre','Activo','fecha_baja',) +class DeviceA24_admin_Serialiazer(serializers.ModelSerializer): + clienteA24 = serializers.SerializerMethodField() + sistema = serializers.SerializerMethodField() + + class Meta: + model = DeviceA24 + fields = '__all__' + + def get_clienteA24(self, obj): + return obj.clienteA24.RFC if obj.clienteA24.RFC else "" + def get_sistema(self,obj): + return obj.sistema.nombre_sistema if obj.sistema.nombre_sistema else "" + class SerialiazerA24(serializers.ModelSerializer): clienteA24 = ClientA24_PKRF(queryset=ClientesA24.objects.all()) sistema = Sistema_A24_PKRF(queryset=Sistema.objects.all()) - token = serializers.CharField(read_only=True) - + token = serializers.CharField(read_only=True) class Meta: model = DeviceA24 fields= ('clienteA24', 'deviceName', 'deviceOS', 'deviceIP', 'token', 'sistema', 'MAC', 'dataBase',) - + def create(self, validated_data): # Extraer los datos obligatorios de la solicitud clienteA24 = validated_data['clienteA24'] diff --git a/IMMEX/urls.py b/IMMEX/urls.py index 5e560f6..b61054e 100644 --- a/IMMEX/urls.py +++ b/IMMEX/urls.py @@ -1,5 +1,10 @@ -from django.urls import path +from django.urls import path, include from . import views +from rest_framework import routers + +router = routers.DefaultRouter() +router.register(r'permisos', views.PermissionListCreateAPIView) +router.register(r'modulos',views.ModulosListCreateAPIView) urlpatterns = [ path('', views.Sistemas_xCliente_IMMEX_ListView.as_view(), name='sistemasXcli_IMMEX'), @@ -14,5 +19,11 @@ urlpatterns = [ path('api/clientes/', views.ClientesA24List.as_view(),name='api_IMMEX_Clientes'), path('api/clientes/detail//', views.ClientesA24Detail.as_view(),name='api_IMMEX_Cliente_detail'), + path('api/admin/devices/', views.DeviceA24List.as_view(), name='api_admin_IMMEX_device-detail'), + path('api/admin/devices//', views.DeviceA24Detail.as_view(), name='api_admin_IMMEX_device-detail'), + + path('api/admin/', include(router.urls)), + + path('api/checar_permisos/',views.ChecarPermisos.as_view(), name='api_ChecarPermisos'), ] \ No newline at end of file diff --git a/IMMEX/views.py b/IMMEX/views.py index 3500591..4b6b80c 100644 --- a/IMMEX/views.py +++ b/IMMEX/views.py @@ -6,10 +6,16 @@ from django.core.mail import send_mail from django.views.generic.edit import CreateView from django.views.generic.list import ListView from django.contrib import messages +from django.db.models import Case, When, Value, BooleanField + +from django.contrib.auth.models import Permission, User +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth import login from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.utils import timezone # Imports de Django REST framework +from rest_framework import viewsets from rest_framework.authentication import TokenAuthentication, BasicAuthentication from rest_framework.views import APIView @@ -19,23 +25,32 @@ from rest_framework.response import Response from rest_framework import status from rest_framework.permissions import IsAuthenticated from rest_framework.pagination import PageNumberPagination -from rest_framework import generics +from rest_framework import generics + # Imports de allauth from allauth.account.models import EmailConfirmation, EmailAddress from allauth.account.forms import SignupForm # Imports de tus modelos y serializadores -from .models import Sistemas_por_cliente_A24, ClientesA24, DeviceA24 +from .permissions import ActiveTokenSessionPerm, TokenCheckSession +from .models import Sistemas_por_cliente_A24, ClientesA24, DeviceA24, ActiveTokenSession, Modulo from Sistemas.models import Sistema, BitacoraErrores from Sistemas.permissions import ItsAdminToken, HasAuthorizationHeader, CheckPermiso from .forms import ClienteForm_IMMEX -from .serializers import ClientesA24Serailizer, SerialiazerA24, SignupSerializer, Sistema_Serializer, Sistema_Por_Cliente_Serializer +from .serializers import (ClientesA24Serailizer, SerialiazerA24, SignupSerializer, + Sistema_Serializer, Sistema_Por_Cliente_Serializer, + DeviceA24_admin_Serialiazer, CustomPermissionSerializer, + ModulosSerializer + ) + # Otras bibliotecas y módulos import urllib.parse import traceback import json +from io import StringIO +import csv class Sistemas_xCliente_IMMEX_ListView(UserPassesTestMixin,LoginRequiredMixin, ListView): model = Sistemas_por_cliente_A24 @@ -85,11 +100,35 @@ class ClientesIMMEX_CreateView(CreateView): else: return response + + """---------API VIEWS---------""" + +class ChecarPermisos(APIView): + authentication_classes = [TokenAuthentication] + permission_classes = [IsAuthenticated, HasAuthorizationHeader, TokenCheckSession] + def get(self,request): + if 'Response-Type' not in request.headers: + Response({"ACCESO":"OK"}) + else: + ct= request.headers['Response-Type'] + response = Response("ACCESS:OK", content_type=ct) + return response + class LoginIMMEX(APIView): authentication_classes = [TokenAuthentication] - permission_classes = [IsAuthenticated, HasAuthorizationHeader] + permission_classes = [IsAuthenticated, HasAuthorizationHeader, ActiveTokenSessionPerm] + def get(self,request): + if 'Response-Type' not in request.headers: + return Response({'username':request.user.username}) + else: + + print(request.headers['Response-Type']) + ct= request.headers['Response-Type'] + response = Response("ACCESS:OK", content_type=ct) + return response + def post(self, request): try: username = request.data.get('username') @@ -119,7 +158,8 @@ class LoginIMMEX(APIView): class RegistroUsuarios(APIView): - permission_classes = [ItsAdminToken] + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[IsAuthenticated,ItsAdminToken] def post(self,request, *args, **kwargs): try: @@ -156,7 +196,9 @@ class RegistroUsuarios(APIView): ) class Check_IMMEX_RFC(APIView): """Verifica que el cliente pueda Timbrar""" - permission_classes = [IsAuthenticated,ItsAdminToken] + + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[IsAuthenticated] def post(self,request,*args, **kwargs): rfc= request.data.get('RFC') @@ -174,6 +216,8 @@ class Check_IMMEX_RFC(APIView): except Exception as E: return Response({'Error':f'check_RFC:{E} RFC:{rfc}','isError':True}) + + class RegisterIMMEX_Device_APIView(APIView): """Register IMMEX Devices se manda el siguiente JSON @@ -189,34 +233,32 @@ class RegisterIMMEX_Device_APIView(APIView): este es un ejemplo, el clienteA24 y sistema deben ser nombres validos para sus tablas en IMMEX, es decir deben estar dados de alta en AS Admin en IMMEX """ - permissions_classes=[IsAuthenticated, ItsAdminToken] + #authentication_classes = (BasicAuthentication, TokenAuthentication, ) + #permission_classes=[ItsAdminToken] def post(self,request): try: serializer = SerialiazerA24(data=request.data, context={'request':request}) if serializer.is_valid(): instance =serializer.save() token = instance.token.key - return Response({'token':token}, status=status.HTTP_201_CREATED) + if 'Response-Type' not in request.headers: + + return Response({'token':token}, status=status.HTTP_201_CREATED) + else: + response = Response(instance.token.key, content_type='text/plain') + return response else: return Response({'Error':f'{serializer.errors}','isError':True}, status=status.HTTP_200_OK) except Exception as ex: data_json = json.dumps(request.data) + traceback_info = f'{data_json}\n{traceback.format_exc()}' BitacoraErrores.objects.create(level=2, message=str(ex), traceback=traceback_info, view='IMMEX.RegisterIMMEX_Device_APIView') return Response({'Error':f'{ex}','isError':True}, status=status.HTTP_200_OK) - - -class Sistemas_IMMEX_List_APIView(APIView): - def get(self, request): - sistemas = Sistema.objects.all() - serializer = Sistema_Serializer(sistemas,many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - -class Sistema_por_cliente_APIView(APIView): def get(self, request): nombre_sistema = request.query_params.get('nombre_sistema') cliente_rfc = request.query_params.get('cliente_rfc') @@ -224,7 +266,7 @@ class Sistema_por_cliente_APIView(APIView): MAC = request.query_params.get('MAC') sistemas_por_cliente = Sistemas_por_cliente_A24.objects.all() - + if nombre_sistema: sistemas_por_cliente = sistemas_por_cliente.filter(id_sistema__nombre_sistema=nombre_sistema) @@ -232,7 +274,71 @@ class Sistema_por_cliente_APIView(APIView): sistemas_por_cliente = sistemas_por_cliente.filter(cliente__RFC=cliente_rfc) dispositivos = DeviceA24.objects.filter(sistema__nombre_sistema=nombre_sistema, clienteA24__RFC=cliente_rfc) + print('dispositivos:',dispositivos) + serializer = Sistema_Por_Cliente_Serializer(sistemas_por_cliente, many=True, + context={'request': request}) + data=serializer.data + for item in data: + del item['id_sistema'] + del item['cliente'] + if 'Response-Type' not in request.headers: + + return Response(data, status=status.HTTP_200_OK) + else: + print(request.headers['Response-Type']) + csv_buffer = StringIO() + + writer = csv.DictWriter(csv_buffer, fieldnames=serializer.child.fields.keys()) + + # Escribe los encabezados del CSV + #writer.writeheader() + + # Escribe los datos en el CSV + for row in data: + + writer.writerow(row) + + # Coloca el puntero del archivo al principio del archivo + csv_buffer.seek(0) + + # Lee la cadena CSV desde el objeto StringIO y devuelve como respuesta HTTP + csv_data = csv_buffer.getvalue() + print(csv_data) + csv_data = csv_data.replace("\r\n", "") + response = Response(csv_data, content_type='text/csv') + return response + +class Sistemas_IMMEX_List_APIView(APIView): + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[IsAuthenticated] + + def get(self, request): + sistemas = Sistema.objects.all() + serializer = Sistema_Serializer(sistemas,many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + +class Sistema_por_cliente_APIView(APIView): + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[IsAuthenticated] + + + def get(self, request): + nombre_sistema = request.query_params.get('nombre_sistema') + cliente_rfc = request.query_params.get('cliente_rfc') + db = request.query_params.get('db') + MAC = request.query_params.get('MAC') + sistemas_por_cliente = Sistemas_por_cliente_A24.objects.all() + + if nombre_sistema: + sistemas_por_cliente = sistemas_por_cliente.filter(id_sistema__nombre_sistema=nombre_sistema) + + if cliente_rfc: + sistemas_por_cliente = sistemas_por_cliente.filter(cliente__RFC=cliente_rfc) + + dispositivos = DeviceA24.objects.filter(sistema__nombre_sistema=nombre_sistema, clienteA24__RFC=cliente_rfc) + serializer = Sistema_Por_Cliente_Serializer(sistemas_por_cliente, many=True, context={'request': request}) data=serializer.data @@ -240,7 +346,8 @@ class Sistema_por_cliente_APIView(APIView): del item['id_sistema'] del item['cliente'] return Response(serializer.data, status=status.HTTP_200_OK) - + + def post(self, request): try: context = { @@ -258,22 +365,20 @@ class Sistema_por_cliente_APIView(APIView): data_json = json.dumps(request.data) traceback_info = f'{data_json}\n{traceback.format_exc()}' - BitacoraErrores.objects.create(level=2, message=str(ex), traceback=traceback_info, + BitacoraErrores.objects.create(level=2, message=str(ex), traceback=traceback_info, \ view='IMMEX.Sistema_por_cliente_APIView') - return Response({'Error':f'{ex}','isError':True}, status=status.HTTP_200_OK) - - + return Response({'Error':f'{ex}','isError':True}, status=status.HTTP_200_OK) #CRUD Clientes IMMEX class MyPage(PageNumberPagination): - page_size = 100 + page_size =1 page_size_query_param = 'page_size' - max_page_size = 100 + max_page_size = 1 class ClientesA24List(generics.ListCreateAPIView): authentication_classes = (BasicAuthentication, TokenAuthentication, ) - permission_classes=[IsAuthenticated,CheckPermiso] + permission_classes=[ItsAdminToken] @@ -302,8 +407,8 @@ class ClientesA24List(generics.ListCreateAPIView): class ClientesA24Detail(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [IsAuthenticated, HasAuthorizationHeader] + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[ ItsAdminToken] def get_object(self, pk): try: @@ -334,3 +439,145 @@ class ClientesA24Detail(APIView): cliente.delete() return Response({"pk":pk},status=status.HTTP_200_OK) +#-----ADMIN AREA +class DeviceA24List(generics.ListCreateAPIView): + #queryset = DeviceA24.objects.all() + serializer_class = DeviceA24_admin_Serialiazer + pagination_class = MyPage + + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[ ItsAdminToken] + def get_queryset(self): + queryset = DeviceA24.objects.all() + # Filtrar por clienteA24 si se proporciona como parámetro de consulta + clienteA24 = self.request.query_params.get('clienteA24') + + + + if clienteA24: + queryset = queryset.filter(clienteA24__RFC__icontains=clienteA24) + + # # Aplicar ordenación si se proporciona como parámetro de consulta + # ordering = self.request.query_params.get('ordering') + + # if ordering: + # queryset = queryset.order_by(ordering) + + return queryset + +class DeviceA24Detail(generics.RetrieveUpdateDestroyAPIView): + queryset = DeviceA24.objects.all() + serializer_class = DeviceA24_admin_Serialiazer + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[ ItsAdminToken] + + # Método para recuperar un registro + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance) + return Response(serializer.data) + + # Método para actualizar un registro + def update(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data) + + # Método para eliminar un registro + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + instance.delete() + return Response(status=204) + + # Método para listar registros (opcional, dependiendo de tus necesidades) + def list(self, request, *args, **kwargs): + queryset = self.get_queryset() + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + +class PermissionListCreateAPIView(viewsets.ModelViewSet): + queryset = Permission.objects.all() + # Asegúrate de tener un serializer adecuado + serializer_class = CustomPermissionSerializer + + authentication_classes = (BasicAuthentication, TokenAuthentication, ) + permission_classes=[ ItsAdminToken] + + + def get_queryset(self): + app_label = self.request.query_params.get('app_label') + user_id = self.request.query_params.get('user_id') + + queryset = Permission.objects.all() + + if app_label: + content_types = ContentType.objects.filter(app_label=app_label) + queryset = queryset.filter(content_type__in=content_types) + + if user_id: + param_user = User.objects.get(id=user_id) + # Anotamos los permisos con True si el usuario los tiene, False en caso contrario + queryset = queryset.annotate( + activo=Case( + When(user=param_user, then=Value(True)), + default=Value(False), + output_field=BooleanField() + ) + ) + + return queryset + + + def list(self, request, *args, **kwargs): + + # Obtén la lista de permisos + queryset = self.get_queryset() + serializer = self.get_serializer(queryset, many=True) + + # Agrega datos personalizados a la respuesta + data = { + "status": "success", + "message": "Lista de permisos recuperada exitosamente", + "data": serializer.data + } + + return Response(data, status=status.HTTP_200_OK) + + def create(self, request, *args, **kwargs): + # Obtén el ID del usuario del JSON de la solicitud + user_id = request.data.get('user_id') + + try: + # Recupera el usuario por su ID + user = User.objects.get(id=user_id) + + # Obtén la lista de permisos del JSON de la solicitud + permissions_data = request.data.get('permissions', []) + + for perm_data in permissions_data: + # Recupera el ID del permiso de cada objeto en la lista + permission_id = perm_data.get('id') + print(permission_id) + try: + # Recupera el permiso por su ID + permission = Permission.objects.get(id=permission_id) + + # Asigna el permiso al usuario + user.user_permissions.add(permission) + except Permission.DoesNotExist: + return Response({"error": f"El permiso con ID {permission_id} no existe"}, status=status.HTTP_400_BAD_REQUEST) + + return Response({"message": "Permisos asignados correctamente al usuario"}, status=status.HTTP_201_CREATED) + except User.DoesNotExist: + return Response({"error": "El usuario no existe"}, status=status.HTTP_400_BAD_REQUEST) + +class ModulosListCreateAPIView(viewsets.ModelViewSet): + queryset = Modulo.objects.all() + pagination_class = MyPage + serializer_class = ModulosSerializer + + def get_queryset(self): + queryset = Modulo.objects.all() + return queryset \ No newline at end of file diff --git a/Sistemas/admin.py b/Sistemas/admin.py index e1ac957..5388a6b 100644 --- a/Sistemas/admin.py +++ b/Sistemas/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Sistema, sistemas_por_cliente, Device,DeviceHistory, BitacoraErrores +from .models import Sistema, sistemas_por_cliente, Device,DeviceHistory, BitacoraErrores class BitacoraErroresAdmin(admin.ModelAdmin): list_display = ['level', 'message','timestamp'] @@ -27,4 +27,4 @@ admin.site.register(BitacoraErrores,BitacoraErroresAdmin) admin.site.register(Sistema,Sistema_Admin) admin.site.register(sistemas_por_cliente,SPC) admin.site.register(Device,DeviceAdmin) -admin.site.register(DeviceHistory,DeviceHistoryAdmin) \ No newline at end of file +admin.site.register(DeviceHistory,DeviceHistoryAdmin) diff --git a/Sistemas/permissions.py b/Sistemas/permissions.py index cfd53a5..37836c8 100644 --- a/Sistemas/permissions.py +++ b/Sistemas/permissions.py @@ -4,6 +4,7 @@ from django.contrib.auth.models import Permission class HasAuthorizationHeader(BasePermission): def has_permission(self, request, view): + return 'Authorization' in request.headers class ItsAdminToken(BasePermission): @@ -18,19 +19,19 @@ class CheckPermiso(BasePermission): def has_permission(self, request, view): user = request.user - print(user) + #print(user) permiso= request.META.get('HTTP_PERMISSION') print('permiso',permiso) - print('es staff: ',user.is_staff) - print('su: ',user.is_superuser) - print('Permisos:', user.user_permissions.filter(codename = permiso)) + #print('es staff: ',user.is_staff) + #print('su: ',user.is_superuser) + #print('Permisos:', user.user_permissions.filter(codename = permiso)) print(user.has_perm(permiso)) - print('Grupos',user.groups.all()) + #print('Grupos',user.groups.all()) if user.is_staff: return True if user.is_superuser: return True - - - return True \ No newline at end of file + if user.has_perm(permiso): + return True + return False \ No newline at end of file