miércoles, 30 de julio de 2025
jueves, 19 de junio de 2025
GLPI (V): Tareas programadas
Introducción
El ENS exige una serie de tareas periódicas para comprobar ciertos niveles de aceptación del sistema:
En el procedimiento 3 de seguridad lógica se establecen planes:
- Anuales (plan de mantenimiento anual)
- Trimestrales:
- SAIs
- Mensuales (?)
- Semanales:
- Servidor: Parches, espacio en disco, antivirus, consumo de memoria ..
- Copias de seguridad
- Cortafuegos (logs, ...)
- Diarias (?)
Manos a la obra
miércoles, 18 de junio de 2025
GLPI (IV) Base de Conocimiento
Tenemos que ir a Herramients (Eines) > Base de Conocimientos y botón de Añadir en la parte superior
Nos pide:
- Categoría de la base de conocimiento. Se puede crear la categoría. Si se quiere modificar hay que buscar en "Encuentra el menu" de la parte superior izquierda e indicarle:
Configuración> Llistas desplegables > Categorías de la base de conocimiento. - Tema
- Contenido a mostrar
- Ficheros de adicionales
- Destinos (a nivel de entidad, grupo, perfil o usuario
viernes, 13 de junio de 2025
GLPI (III) Formularios
Se ha seguido el vídeo de Paul Portales
1. Agregar el plugin
Según Paul Portales se debe ir a :
Configuración >Plugins (o complementos) > Tienda (si no se ha registrado hay que registrarse) >Botón de duscubrir
Pero para instalar el plugin es mejor seguir estos pasos:
1. Ir a la página de descargas del plugin
https://github.com/pluginsGLPI/formcreator/releases
2. Descargar una versión compatible que está en formato zip y descomprimirla en una carpeta. Renombrar esta carpeta si es necesario a "formcreator"
3. Copiar esta carpeta "formcreator" dentro de la carpeta de plugins de GLPI
/var/www/html/glpi/plugins/
sudo chown -R www-data:www-data /var/www/html/glpi/plugins/formcreator
sudo chmod -R 755 /var/www/html/glpi/plugins/formcreator
4. Instalar el plugin desde la interfaz de GLI. Para ello inicia sesión en GLPI como administrador.
Ve a Configuración > Plugins. Busca el plugin Formcreator en la lista.
Haz clic en Instalar. Luego, haz clic en Habilitar.
2. Crear un formulario
3. Agregar Campos (Preguntas)
- Nombre del campo
- La sección donde se va a incluir
- Si su relleno es obligaorio (no puede ir en blanco)
- Tipo de campo:
- Actor (glpi,normal,post-only,tech,informatic y Plugion_GLPI_Inventory)
- Additional Fields (No está acivado el componente para este tipo de campos)
- DIrecciones IP
- Campo oculto (como es oculto, hay que darle un valor por defecto)
- CheckBoxes ( y le damos cada una de las opciones separadas por salto de línea)
- Fecha
- Fecha y hora
- Descripcion (Campo de solo lectura, por ejemplo las condiciones del contrato)
- Enter donde se indica una expresión regular que dicho campo tiene que validar
- Fichero (para adjuntar uno varios ficheros)
- Float (que también pide una expresión regular)
- Hostname
- LDAP Select: Pide un Directorio de LDAP, un filtro y un atributo (Como (AD) User ID
- Lista desplegable (Donde se elige una lista desplegable existente)
- MultiSelet (Se le da los valores separados consalto de línea)
- Objeto GLPI (Pide el tipo de objeto, ordenadores, programas, contratos ...)
- Select (Se le da valores con salto de línea)
- Tags ???
- Hora
- Text
- TextArea
- Tipo de petición (Incidencia, Solicitud)
- Urgencia (Alta, baja,...)
- Valor por defecto
- Descripción detallada
- Expresión regular (solo en algunos tipos de campos)
- Máximo y mínimo número de caractres del campo
4. Generar tickets (Objetivos)
- Entidad de destino, tiempo que debe responder el tiquet, SLA y OLA (acuerdos de nivel de servicio) ...
5. Tratamiento de respuestas de formulario
6. Generar un PDF del Formulario
- Darle al icono de la imagen
- Nos pide un nombre, el cual será el nombre simple del fichero de la imagen con su extensión (sin su ruta), por ejemplo logo01.jpg y opcionalemente una descripción alternativa y tamaño horizontal y vertical
- Una vez añadido, nos vamos al icono "<>" para ver el código fuente y vemos que hay un fragmento de código que dice:
<img src="http://192.168.XXX.XXX/glpi/plugins/formcreator/front/logo01.jpg" alt="Prova Edu" width="50" height="100"> - Como en nuestro caso se ha instalado el glpi en la ruta:
/var/www/glpi del servidor, tenemos que copiar el fichero de la imagen a la carpeta indicada pero con la siguiente ruta:
/var/www/glpi/plugins/formcreator/front
pero hay que cambiarle el usuario a www-data y el grupo a www-data al fichero copiado con el comando chown
martes, 10 de junio de 2025
GLPI (II). Chuleta de utilización
1. Uso básico
1.1 Acceso
http://192.xxx.xxx.xxx/glpi
usuario glpi
contraseña: mi_contraseña
2. Gestión de usuarios
En Administración > Usuarios > Acciones - Botón Añadir Usuario
Se le indica nombre, login, email, perfil (técnico, admin, usuario,..), Entidad
Desde el usuario en la pestaña perfiles se le pueden añadir múltiples permisos por entidad
3. Gestión de entidades
En Administración > Entidades
Se puede configurar la separación de usuarios y formularios
4. Gestión de tickets
En Asistencias > Tickets
Se pueden crear tickets, asignar a un técnico, cambiar estado, definir reglas de automatización
5. Formularios personalizados
En Configuración > Formularios
Crear campos adicionales por tipo (ticket, equipo, usuario ..)
Utilizar campos tipo (texto, números, listas estáticas y dinámicas
6. Inventario de equipos
Para ello hay que instalar el agente a cada ordenador.
lunes, 9 de junio de 2025
GPLI (I). Instalar el agente en Ubuntu y Windows. Crear política de grupo
1. Instalar el agente en Ubuntu
Tenemos un ciente Ubuntu (no un servidor) y queremos que aparezca en el inventario.
Vamops a la URL
https://github.com/glpi-project/glpi-agent/releases
y descargamos:
Ahora ejecutamos:
sudo dpkg -i glpi-agent_1.14-2_all.deb
y nos da algunos errores. Para solucionarlo ejecutamos:
sudo apt-get install -f --fix-missing
y volvemos a ejecutar:
sudo dpkg -i glpi-agent_1.14-2_all.deb
Y probamos a ver si funciona:
sudo systemctl status glpi-agent
y nos da error:
Loaded: loaded (/usr/lib/systemd/system/glpi-agent.service; enabled; preset: enabled)
Active: failed (Result: exit-code) since Mon 2025-06-09 13:47:51 CEST; 12s ago
Duration: 144ms
Docs: man:glpi-agent
Process: 550431 ExecStart=/usr/bin/glpi-agent --daemon --no-fork $OPTIONS (code=exited, status=1/FA>
Main PID: 550431 (code=exited, status=1/FAILURE)
CPU: 111ms
de juny 09 13:47:51 a03-inf-005 systemd[1]: Started glpi-agent.service - GLPI agent.
de juny 09 13:47:51 a03-inf-005 glpi-agent[550431]: [error] No target defined, aborting
de juny 09 13:47:51 a03-inf-005 systemd[1]: glpi-agent.service: Main process exited, code=exited, statu>
de juny 09 13:47:51 a03-inf-005 systemd[1]: glpi-agent.service: Failed with result 'exit-code'.
Para solucionarlo, modificamos el fichero de configuración:
sudo nano /etc/glpi-agent/agent.cfg
y añadimos la línea
server = http://192.168.28.28/glpi
guardamos y rearrancamos el servicio y vemos el estado
sudo systemctl start glpi-agent
sudo systemctl status glpi-agent
y funciona bien
Loaded: loaded (/usr/lib/systemd/system/glpi-agent.service; enabled; preset: enabled)
Active: active (running) since Mon 2025-06-09 14:27:54 CEST; 4s ago
Docs: man:glpi-agent
Main PID: 569553 (glpi-agent: wai)
Tasks: 1 (limit: 38323)
Memory: 62.8M (peak: 62.9M)
CPU: 343ms
CGroup: /system.slice/glpi-agent.service
└─569553 "glpi-agent: waiting"
2. Instalar el agente en Windows
Vamos a esta URL
https://github.com/glpi-project/glpi-agent/releases
y descargamos
Lo ejecutamos como administrador, damos todos los valores por omisión excepto la dirección del servidor que le damos:
http://192.168.28.28/glpi
Cuando se ha instalado ya hay que ediar este fichero en modo administrador:
C:\Program Files\GLPI-Agent\etc\agent.cfg
y añadimos la línea
server = http://192.168.28.28/glpi
y guardamos.
Forzamos el servicio con el modo administrador:
glpi-agent.bat --debug --force
puede que de algunos avisos sin importancia como
C:\Program Files\GLPI-Agent>glpi-agent.bat --debug --force Use of uninitialized value $category in array element at C:/Program Files/GLPI-Agent/perl/agent/GLPI/Agent/Task/Inventory/Win32/Firewall.pm line 115.
Pero lo importnte es ver si se ha aparecido en el GLPI en la parte de ordenadores
3. Instalar con política de grupo GPO
1. Primeramente se crea una capeta compartida en SYSVOL por ejemplo XIMO-GLPI-agent-deployment. La ruta de esta carpeta vista desde el servidor de dominio es:
C:\Windows\SYSVOL\sysvol\<TuDominio>\Policies\XIMO-GLPI-agent-deployment
donde <TuDominio> es el nombre que se le haya dado al dominio
2. Se copia el GLPI-Agent-1.14-x64.msi a dicha carpeta. Para ello se utiliza xcopy en una consola ejecutada como administrador.
3. Crear un archivo de transformación (MST) para la configuracion del servidor GLPI
1. Primeramente se guarda el programa GLPI_Agent-1.14-x64.msi en una carpeta del sistema que todo el mundo pueda acceder.
2. Se crea este programa instalar_glpi_agent.bat en una ruta compartida
@echo off msiexec /i "\\servidor\instaladores\GLPI-Agent-1.14-x64.msi" /qn /norestart SERVER=http://192.168.28.28/glpi
3. Se crea una política de grupo:
3.1 Abre GPMC (Group Policy Management Console).
3.2 Crea una nueva GPO: Instalación GLPI Agent.
3.3 Edita la GPO y ve a:
Configuración del equipo > Configuración de Windows > Scripts (Inicio/Apagado) > Inicio
3.4 Añade el script instalar_glpi_agent.bat desde la ruta compartida.
3.5 Aplica la GPO a las unidades organizativas (OU) con tus ordenadores.
3.6 Forzar actualización de GPO. En los clientes, puedes probar con:
gpupdate /force
jueves, 15 de mayo de 2025
OSTicket (2) Estructura de la BD
1. Relación de tablas:
Estas son las tablas:
ost_api_key
ost_attachment
ost_canned_response
ost_config
ost_content
ost_department
ost_draft
ost_email
ost_email_account
ost_email_template
ost_email_template_group
ost_event
ost_faq
ost_faq_category
ost_faq_topic
ost_file
ost_file_chunk
ost_filter
ost_filter_action
ost_filter_rule
ost_form
ost_form_entry
ost_form_entry_values
ost_form_field
ost_group
ost_help_topic
ost_help_topic_form
ost_list
ost_list_items
ost_lock
ost_note
ost_organization
ost_organization__cdata
ost_plugin
ost_plugin_instance
ost_queue
ost_queue_column
ost_queue_columns
ost_queue_config
ost_queue_export
ost_queue_sort
ost_queue_sorts
ost_role
ost_schedule
ost_schedule_entry
ost_sequence
ost_session
ost_sla
ost_staff
ost_staff_dept_access
ost_syslog
ost_task
ost_task__cdata
ost_team
ost_team_member
ost_thread
ost_thread_collaborator
ost_thread_entry
ost_thread_entry_email
ost_thread_entry_merge
ost_thread_event
ost_thread_referral
ost_ticket
ost_ticket__cdata
ost_ticket_priority
ost_ticket_status
ost_translation
ost_user
ost_user__cdata
ost_user_account
ost_user_email
martes, 6 de mayo de 2025
OSTicket (I) API
1. Introducción
La API de OsTicket es poco funcional ya que de momento solo permite crear tickets.
Para utilizar la API de OsTicket hay que primeramente crear una clave
Existen proyectos para dar mas funcionalidad a la API como la de BMSVieira que está en Github y en documentación. Pero no he podido hacerlo funcionar
2. Creación de una clave para usar la API
Nos vamos a Tauler de l'administrador
Nos vamos Administració-API y le indicamos el campo Adreça IP
Y obternemos una clave para una determinada IP3. Crear un ticket
import requests def display_response(response): try: print(response.json()) except Exception: print(response.text) def get_new_tiket_old(): headers = { "X-API-Key": "XXXXXXXXXXXX", "Content-Type": "application/json", # Lo que envío "Accept": "application/json" # Lo que quiero recibir } data = { "name": "Ximo", "email": "ximodante@gmail.com", "subject": "2ª Prueba llamada a API de creación de tickets", "message": "A ver si crea el ticket por 2ª vez", } url='http://192.168.XXX.XXX/osticket/api/http.php/tickets.json' response = requests.post(url, json=data, headers=headers) display_response(response) if __name__ == "__main__": get_new_tiket_old()
4. Instalacion de BMSVieira API (no funciona!!!)
scp -r ./ost_wbs usuario@ip_del_servidor:/var/www/osticket
miércoles, 30 de abril de 2025
Gitlab(5): Problemas
1. Después de funcionar bien VS con gitlab durante una temporada, se produce este error:
Node.js v20.18.3
git@gitlab.municipio.es: Permission denied (publickey,password).
fatal: No s'ha pogut llegir del repositori remot.
Assegureu-vos que tingueu els permisos
d'accés correctes i que el repositori existeixi.
SOLUCIÓN: Parece ser que se ha perdido la clave ssh. Para ello, se busc el nombre del fichero de clave ssh (en mi caso es id_ed25519.ximo) y se ejecuta:
ssh-add ~/.ssh/id_ed25519.ximo
NOTA: Cuando se crea un usuario dentro de Gitlab, se le puede crear una clave ssh para que no vuelva a pedir más la contraseña. Esta clave se guarda en la carpeta ~/.ssh
Esto sucede si en VS se ha configurado para acceder a gitlab con ssh. Para comprobar que estamos accediendo con ssh se ejecuta dentro de la carpeta principal del proyecto
git remote -v
martes, 29 de abril de 2025
INAP curso seguridad (II) Introducción al ENS
1. Definiciones:
- Política de seguridad:
- Principios:
- Asegurar acceso
- Confidencialidad
- Integridad
- Trazabilidad
- Autenticidad
- Disponibilidad
- Conservación de datos
- Requisitos mínimos:
2. Principios básicos del ENS
- Seguridad como proceso integeral
- Gestión de la seguridad basada en riesgos
- Prevención, detección, respuesta y conservacion
- Gerstión de incidentes
- Existencia de líneas de defensa en varias capas
- Vigilancia continua
- Reevaluación periódica
- Diferenciación de responsabilidades:
- 3 niveles:
- Gobierno (dirección)
- Supervisión (RESEG responsable de seguridad y DPDP delegado de protección de datos).
- Operativo (RS Responsable de sistemas, AD Administrador de seguridad y OS Operadores del sistema)
- 3 roles: Dirección , responsable de información y responsable de servicio
- Otros roles: Responsable de seguridad física, Responsable de gestión de personal, responsable de contratación y adquisición, usuarios del sistema.
- No se puede ser responsable de sistemas y de seguridead
- La política de seguridad detallará las atribuciones de cada rol
- 4 responsables:
- Información
- Servicio
- Seguridad
- Sistema (pueden ser varias personas delegadas)
lunes, 28 de abril de 2025
INAP curso seguridad (I) Protección de datos personales
Créditos: https://sede.inap.gob.es/proteccion-de-datos-personales
1. Fuentes de derecho
Constitución española. Su artículo 18.4 otorga el carácter de derecho fundamental a la protección de datos de carácter personal.
Ley Orgánica 3/2018, de 5 de diciembre, de Protección de Datos Personales y garantía de los derechos digitales.
Reglamento (UE) n.º 2016/679 del Parlamento Europeo y del Consejo, de 27 de abril de 2016, relativo a la protección de las personas físicas en lo que respecta al tratamiento de datos personales y a la libre circulación de estos datos y por el que se deroga la Directiva 95/46/CE
2. Responsable del tratamiento de los datos de carácter personal (RTDP)
3. Delegado de protección de datos (DPD)
4. Registro de actividades de tratamiento
- Gestión tributaria y recaudatoria
- Gestión de Personal
- Servicios sociales
- Nombre de la actividad
- Fecha de actualización
- Responsable de tratamiento (normalmente es el responsable del tratamiento (visto en el punto 2)
- Corresponsable: Normalmente la persona o departamento encargado de dicha actividad
- Delegado de protección de datos (normalmente es la persona del punto 3)
- Fines del tratamiento. La gestión de dicha actividad
- Base legal: Normalmente la legislación que ampara dicha actividad (reglamento de reaudación, etc)
- Catagoría de los interesados: Personas físicas, jurídicas ...
- Categoría de los datos personales: (Datos identificativosm y de contacto, datos académicos, datos fiscales,..)
- Categoría de los destinatarios:(Bases dwe convocatorias etc)
- Transferencias internacionales: (solo cuando sw precean)
- Medidas técnicas y organizativas de seguriead:Normalmente:Las medidas de seguridad corresponden a las aplicadas de acuerdo al Anexo II (Medidas de seguridad) del Real Decreto 3/2010, de 8 de enero, por el que se regula el Esquema Nacional de Seguridad en el ámbito de la Administración Electrónica.
5. Derechos del interesado. Vías para ejercer los derechos
- RTDP por vía electrónica o Red de oficinas en materia de registro (https://administracion.gob.es)
- DPD (art. 38.4 RGPD)
- AEPD (Agencia Española de protección de Datos) para reclamaciones
jueves, 24 de abril de 2025
Certificados: Instalar el certificado raiz en Chrome, Mozilla, Edge y Brave
1. Windows (Resto de navegadores -Chrome, Edge, Brave ..)
Crear/Editar GPO
Abra Administración de Directivas de Grupo (gpmc.msc)
Vincule un GPO nuevo/existente a la OU objetivo
Importar certificado al almacén del sistema
Navegue a:
Configuración del equipo → Políticas → Configuración de Windows → Configuración de seguridad → Directivas de clave pública → Entidades de certificación raíz de confianzaClick derecho → Importar
Seleccione el archivo .cer desde una ruta de red accesible (ej: \\servidor\certificados\rootCA.cer)
Opciones clave de implementación
Almacén destino: Autoridades de Certificación Raíz de Confianza
Actualización automática: 90-120 minutos (default) o forzar con gpupdate /force
Verificación en Edge
Acceda a edge://settings/privacy → Administrar certificados
En Autoridades de certificación raíz, confirme la presencia del certificado
Notas técnicas:
Edge usa el almacén de certificados de Windows (Cert:\LocalMachine\Root)
Formatos compatibles: .cer, .crt, .der (evite .pem sin conversión)
Para instalaciones masivas:
certutil -f -addstore "Root" \\network\path\rootCA.cer
Este método garantiza compatibilidad con Edge 94+ y versiones Chromium-based, sin requerir configuraciones adicionales en el navegador[1][2][3].
⁂
2. Mozilla Firefox en Windows
- Descarga las plantillas oficiales de Mozilla: https://github.com/mozilla/policy-templates/releases
- Copia los .admx en C:\Windows\PolicyDefinitions y los .adml (de tu idioma, ej. es-ES) en C:\Windows\PolicyDefinitions\es-ES\.
- Configuración de equipo
- Directivas
- Plantillas administrativas: definiciones de equipo
- Mozilla
- Firefox
- Certificates
- Import Enterprise Roots : Habilitada
3. Corrección de Problemas
NO USAR LO QUE VIENE A CONTINUACIÓN
1.1 Idea principal
Se tiene que crear el fichero "policies.json" en la carpeta de instalacioón del Mozilla Firefox:
"C:\Program Files\Mozilla Firefox\distribution\"
que tendrá este contenido:
{
"policies": {
"Certificates": {
"Install": ["C:\\ruta\\certificado\\rootCA.cer"]
}
}
}
1.2 Utilizar una GPO
Veamos el fichero power shell que se ejecuta como administrador de la máquina, y busca en las posibles rutas de instalación de Firefox y si lo encuentra crea este fichero para que el certificado raiz se instale en el Mozilla Firefox:
#Requires -RunAsAdministrator
# 1. Posibles rutas de instalación de firefox
$firefoxPaths = @(
"C:\Program Files\Mozilla Firefox",
"C:\Program Files (x86)\Mozilla Firefox"
)
# 2. Ruta del certificado a instalar
$certPath = "\\servidor\red\certificados\rootCA.pem"
# 3. Verificar ejecución como administrador
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Error "Ejecutar como Administrador"
exit 1
}
try {
# 4. Buscar instalación existente de Firefox
$firefoxInstallPath = $firefoxPaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($firefoxInstallPath) {
$distPath = Join-Path $firefoxInstallPath "distribution"
if (-Not (Test-Path $distPath)) {
New-Item -ItemType Directory -Path $distPath | Out-Null
}
# 5. Contenido del fichero json para copiar el certificado al directorio de distribución
$jsonContent = @"
{
"policies": {
"Certificates": {
"Install": ["$certPath"]
}
}
}
"@
# 6. Escribir el fichero json en el directorio de distribución
# Para que al rearrancar firefox se instale el certificado
$jsonContent | Out-File -FilePath "$distPath\policies.json" -Encoding utf8 -Force
# 7. Copiar el certificado al directorio de distribución
Copy-Item -Path $certPath -Destination $distPath -Force
# 8. Crear el fichero policies.json en el directorio de distribución
# y escribir el contenido del json en él
$jsonContent | Set-Content -Path "$distPath\policies.json" -Force
Write-Output "Política aplicada en: $distPath\policies.json"
}
else {
Write-Warning "Firefox no está instalado en rutas estándar"
}
}
catch {
Write-Error "Error en la implementación: $_"
exit 1
}
ahora guardamos el fichero como Deploy-FirefoxCert.ps1
Ahora vamos al Windows 2019 server y en el Administrador de Directivas de Grupo:
- Crear nueva GPO > Editar
- Navegar a: Configuración del equipo/Preferencias/Configuración de Windows/Tareas programadas
- Crear tarea:
- Acción: Iniciar programa
- Programa/script: powershell.exe
- Argumentos: -ExecutionPolicy Bypass -File "\\ruta\netlogon\Deploy-FirefoxCert.ps1"
- Ejecutar con privilegios más altos: Habilitado
martes, 22 de abril de 2025
Python(XXVII). Beforeware. Aplicando el ENI (II). Comprobación de las entradas de datos. FastHTML beforeware
1. Beforeware en FastHTML
Cuando creamos una aplicacion FastHTML utilizando app = FastHTML(), podemos pasar un parámetro que es "before=Beforeware(función)" siendo "función" el nombre de una función que hemos definido para ejecutarse justo antes de ejecutar una ruta. Si la función devuelve "None", la petición sigue normalmente. Andreas Stokl lo explica muy bien.
Uno de nuestros objetivos a verificar es que el "tab_id" sea el correcto y que tengamos un usuario correcto.Para ello, sino se cumplen estos requisitos, se redirigirá a la pantalla de login.
Para ello se define una función que será la del beforeware:
#2. Beforeware function to be executed in all endpoints def my_before_function(req, tab_id, sess): ''' Beforeware function: checks that the user is authenticated ''' print('Des de beforeware, la URL es:', req.url) # 1. Validem que haja tab_id i que l'usuari estiga en la sessió de la BD my_response,myerror=xmsession.validate(tab_id=tab_id, req=req) if (len(myerror.strip())>1): print ("redirecting from my_before_function " ) return my_response
Otra cosa importante, es decirle al beforeware que regex hay que saltarse "skip"para que no haga comprobaciones en cada caso. En AnswerDotAi se puede ver mirando el comentario del código.
# To create a Beforeware object, we pass the function itself, and optionally a list of regexes to skip. my_before_function = Beforeware(my_before_function, skip=[r'/favicon\.ico', r'/static/.*', r'.*\.css', '/login'])
Ahora hay que indicarselo a la aplicación:
app = fh.FastHTML( hdrs=my_headers, before=my_before_function, secret_key=xmother.new_id(25), exception_handlers=exception_handlers)
2. Lo que se puede hacer con beforeware:
- Guardar para auditar en la BD, las peticiones (url, usuario, fecha y hotra, IP ...)
- Verificar si el usuario tiene permisos para acceder a dicha URL
- Detectar si hay código malicioso en la petición (inyección de SQL ...)
martes, 15 de abril de 2025
Python (XXVI) Sqlalchemy: Localizar e importar todas las clases a persistir. Modificar las tablas cuando la clase asociada cambia
1. Importar todas las clases a persistir con sqlalchemy
Para ello debemos tener todos los módulos que definen las clases dentro de una carpeta, en mi caso "models"
Creamos un módulo dentro de la carpeta "models" que recoja toda las clases y así solo tenemos que importar el array de clases a persistri.
El módulo se llama xmallmodels.py, y la variable que contendrá la clases es model_classes y su código es:
import importlib import pkgutil import inspect #------Imprescindible para poder importar de otras carpetas (de basicutils) import sys from pathlib import Path path_root1 = Path(__file__).parents[1] # Damos un salto de directorio hacia arriba-> ximoutilsmod sys.path.append(str(path_root1)) path_root0 = Path(__file__).parents[0] # El mismo directorio sys.path.append(str(path_root0)) from basicutils.xmdb import Base import models # ------Fin imprescindible model_classes = [] for loader, module_name, is_pkg in pkgutil.iter_modules(models.__path__): module = importlib.import_module(f"models.{module_name}") for name, obj in inspect.getmembers(module, inspect.isclass): # Ensure it's defined in the current module and is a subclass of Base (but not Base itself) if obj.__module__ == module.__name__ and issubclass(obj, Base) and obj is not Base: model_classes.append(obj) # Example: register or print them for cls in model_classes: print(f"Found model class: {cls.__name__} from {cls.__module__}")
Por ejemplo en nuestro programa de arranque, que intenta vincular los modelos con la BD, basta con importar model_classes desde este módulo. El código de este módulo mnu_main.py es:
#!/home/eduard/MyPython/11.softprop-01/venv_softprop/bin/python3 #1. Imports from sqlalchemy import Table #------Imprescindible para poder importar de otras carpetas (de basicutils) import sys from pathlib import Path path_root1 = Path(__file__).parents[1] # Damos un salto de directorio hacia arriba-> ximoutilsmod sys.path.append(str(path_root1)) path_root0 = Path(__file__).parents[0] # El mismo directorio sys.path.append(str(path_root0)) from menus.mnu_fh import fh, app #OJO: No eliminar las dependencias marcadas en gris (routes01mnu, routes02form, routes03grid,app) pues, sinó falla el programa from menus import routes00comp, routes01mnu, routes02form, routes03grid # No eliminar !!! from menus.mnu_fh import fh, app # No eliminar app !!! from basicutils import xmdb #--- Definición de tablas de la Base de datos postgres from models.xmallmodels import model_classes # ------Fin imprescindible from basicutils import xmdb # Create database tables xmdb.Base.metadata.create_all(bind=xmdb.engine) # Execute the web server, but now not using fh.serve but uvicorm.run instead #fh.serve() if __name__ == "__main__": import uvicorn app.mount("/static", fh.StaticFiles(directory="/home/eduard/MyPython/11.softprop-01/static"), name="static") #uvicorn.run(app, host="0.0.0.0", port=5001, # ssl_keyfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.rsa", # ssl_certfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.crt") cert_path="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard.municipio.es." #uvicorn.run(app, host="edu.tavernes.es", port=5001, uvicorn.run(app, host="192.168.XXX.XXX", port=5001, ssl_keyfile =cert_path+"key", ssl_certfile=cert_path+"crt")
2. Modificar las tablas cuya clase asociada cambia
Para ello se buscan las clases que deriven de la clase Base, y si estan asociadas a una tabla (tienen el campo __tablename__) y se compara los atributos de la clase con las columnas de la tabla; y se añaden columnas o se eliminan en base a los atributos de las clases.
Veamos el código del módulo xmdbupdate.py :
''' Actualización de la estructura de tablas en función de las clases asociadas de SQLAlchemy. ''' from sqlalchemy.sql import text from sqlalchemy.inspection import inspect from sqlalchemy.exc import ProgrammingError #------Imprescindible para poder importar de otras carpetas (de basicutils) import sys from pathlib import Path path_root1 = Path(__file__).parents[1] # Damos un salto de directorio hacia arriba-> ximoutilsmod sys.path.append(str(path_root1)) path_root0 = Path(__file__).parents[0] # El mismo directorio sys.path.append(str(path_root0)) #OJO: No eliminar las dependencias marcadas en gris #--- Definición de tablas de la Base de datos postgresfrom basicutils.xmdb import Base from basicutils import xmdb from models.xmallmodels import model_classes # ------Fin imprescindible #----------------------------------------------------------- # --- Configuration --- session = xmdb.session_local() def all_table_subclasses(cls): """Recursively find all subclasses of a class that has a table assigned.""" subclasses = set() for subclass in cls.__subclasses__(): print (subclass.__name__,getattr(subclass, '__tablename__', ''), getattr(subclass, '__abstract__', False)) if len(getattr(subclass, '__tablename__', '')) > 0: subclasses.add(subclass) subclasses.update(all_table_subclasses(subclass)) return subclasses # --- Update function --- def update_table_structure(cls): table_name = cls.__tablename__ table_args = cls.__table_args__ schema = table_args.get('schema') if isinstance(table_args, dict) else None inspector = inspect(xmdb.engine) if not inspector.has_table(table_name, schema=schema): print(f"Table '{schema}.{table_name}' does not exist. Creating it...") cls.__table__.create(xmdb.engine) return db_columns = {col["name"]: col for col in inspector.get_columns(table_name, schema=schema)} model_columns = {col.name: col for col in cls.__table__.columns} #Execute a transaction with xmdb.engine.begin() as conn: # Add missing columns for name, column in model_columns.items(): if name not in db_columns: col_type = column.type.compile(xmdb.engine.dialect) nullable = "NULL" if column.nullable else "NOT NULL" alter = f'ALTER TABLE "{schema}"."{table_name}" ADD COLUMN "{name}" {col_type} {nullable};' print("Adding:", alter) conn.execute(text(alter)) # Drop extra columns for name in db_columns: if name not in model_columns: alter = f'ALTER TABLE "{schema}"."{table_name}" DROP COLUMN "{name}";' print("Dropping:", alter) try: conn.execute(text(alter)) except ProgrammingError as e: print(f"Could not drop column {name}: {e}") # --- Run for all subclasses of Base for updating the tables--- def update_all_tables(base): for cls in all_table_subclasses(base): update_table_structure(cls) if __name__ == "__main__": update_all_tables(xmdb.Base) session.close() xmdb.engine.dispose()