Las normas expuestas son de obligado cumplimiento. La STIC podrá estudiar los casos excepcionales los cuales serán gestionados a través de los responsables del proyecto correspondiente y autorizados por el Área de Gobernanza de la STIC. Asimismo cualquier aspecto no recogido en estas normas deberá regirse en primera instancia por las guías técnicas correspondientes al esquema nacional de seguridad y esquema nacional de interoperabilidad según correspondencia y en su defecto a los marcos normativos y de desarrollo software establecidos por la Junta de Andalucía, debiendo ser puesto de manifiesto ante la STIC.
La STIC se reserva el derecho a la modificación de la norma sin previo aviso, tras lo cual, notificará del cambio a los actores implicados para su adopción inmediata según la planificación de cada proyecto.
En el caso de que algún actor considere conveniente y/o necesario el incumplimiento de alguna de las normas y/o recomendaciones, deberá aportar previamente la correspondiente justificación fehaciente documentada de la solución alternativa propuesta, así como toda aquella documentación que le sea requerida por la STIC para proceder a su validación técnica.
Contacto Arquitectura: l-arquitectura.stic@juntadeandalucia.es
Los cambios en la normativa vendrán acompañados de un registro de las modificaciones. De este modo se podrá realizar un seguimiento y consultar su evolución. Ordenándose de mas recientes a menos recientes, prestando especial cuidado a las cabezeras de la tablas dónde se indican las fechas de entrada en vigor y versión.
| Versión | v01r00 | Fecha publicación |
| Fecha entrada en vigor | |
| Alcance | |||||
| |||||
En cumplimiento de la LOPD, los sistemas de información realizados en el contexto del Servicio Andaluz de Salud están obligadas a realizar una serie de tareas de auditoría que deben ser implementadas por la totalidad de los productos realizados en la organización.
El término de Auditoría se ha empleado incorrectamente con frecuencia ya que se ha considerado como una evaluación cuyo único fin es detectar errores y señalar el fallo, o dicho en otras palabras, con frecuencia se ha denominado Auditoría al proceso de registro de eventos que sucede en una aplicación con el fin de poder depurarla para detectar errores.
El sistema de registro de Auditoría de un sistema de información debe ir encaminado a registrar la activdad del sistema con el fin de poder analizar la información en una fase posterior, pudiendo evaluar la calidad del dato y actuando en consecuencia para mejorar la eficacia y eficiencia de un sistema.
Dicho esto, podemos decir que la Auditoría Informática de Sistemas de Información es el proceso de recoger, agrupar y evaluar evidencias para determinar si un Sistema de Información salvaguarda el activo empresarial, mantiene la integridad de los datos, lleva a cabo eficazmente los fines de la organización, utiliza eficientemente los recursos, y cumple con las leyes y regulaciones establecidas.
Partiendo de las definiciones anteriormente mencionadas, los sistemas de información deben centrarse en auditar las operaciones que se solicitan al sistema, es decir, acciones que realiza el usuario directamente desde la interfaz de usuario, peticiones a servicios por parte de terceras aplicaciones, etc. No siendo necesario auditar las operaciones internas que el sistema informático pueda realizar para llevar cabo la auditoria (Llamadas a base de datos, llamadas entre capas internas, accesos a serivcios web externos, etc.)
De esta forma, se definen los siguientes datos básicos que deben ser auditados cada vez que el usuario realiza una acción sobre el sistema:
Adicionalmente, se definen una serie de parámetros a auditar que, no siendo básicos en un sistema de auditoría tradicional, están presentes en la mayoría de los sistemas del SAS, por lo que se han tomados como básicos para la organización.
Finalmente, dependiendo de la acción concreta que se esté auditando, se deberán registrar también aquella información adicional que se considere clave para identificar la operación que se está auditando.
Un ejemplo de información adicional a auditar podría ser el caso de una búsqueda de pacientes donde el usuario puede realizar una consulta por apellidos y edad. En este caso los campos a auditar serían, además de los mencionados previamente, el apellido introducido y la edad buscada.
Las normas expuestas son de obligado cumplimiento. La STIC podrá estudiar los casos excepcionales los cuales serán gestionados a través de los responsables del proyecto correspondiente y autorizados por el Área de Gobernanza de la STIC. Asimismo cualquier aspecto no recogido en estas normas deberá regirse en primera instancia por las guías técnicas correspondientes al esquema nacional de seguridad y esquema nacional de interoperabilidad según correspondencia y en su defecto a los marcos normativos y de desarrollo software establecidos por la Junta de Andalucía, debiendo ser puesto de manifiesto ante la STIC.
La STIC se reserva el derecho a la modificación de la norma sin previo aviso, tras lo cual, notificará del cambio a los actores implicados para su adopción inmediata según la planificación de cada proyecto.
En el caso de que algún actor considere conveniente y/o necesario el incumplimiento de alguna de las normas y/o recomendaciones, deberá aportar previamente la correspondiente justificación fehaciente documentada de la solución alternativa propuesta, así como toda aquella documentación que le sea requerida por la STIC para proceder a su validación técnica.
Contacto Arquitectura: l-arquitectura.stic@juntadeandalucia.es
Los cambios en la normativa vendrán acompañados de un registro de las modificaciones. De este modo se podrá realizar un seguimiento y consultar su evolución. Ordenándose de mas recientes a menos recientes, prestando especial cuidado a las cabezeras de la tablas dónde se indican las fechas de entrada en vigor y versión.
| Versión | v01r12 | Fecha publicación | Fecha entrada en vigor | | |
| Alcance | |||||
| |||||
| Versión | v01r13 | Fecha publicación |
| Fecha entrada en vigor | |
| Alcance | |||||
| |||||
Con el objetivo de facilitar el cumplimiento de la normativa del SAS en cuanto auditoría se refiere, desde el área de gobernanza de la STIC se proporcionan dos posibles soluciones basadas en componentes que permitan a los desarrolladores incorporar todo el sistema de auditorías en sus aplicaciones de forma rápida y estandarizada, minimizando el tiempo necesario que se requiere para realizar este proceso.
En el SAS coexisten actualmente dos soluciones para integrar auditoría en aplicaciones. Esta página documenta ambas alternativas y establece los criterios de elección conforme a la normativa técnica vigente y a los principios de la arquitectura de referencia del SAS.
El objetivo principal de Audita es disponer de un sistema de información independiente, único y centralizado para la consolidación y registro de todos los logs de aplicación auditoría de negocio de todos los sistemas de información del SAS independientemente de si son asistenciales, económico financieros, de recursos humanos o cualquier otro ámbito. Con esto se pretende consolidar en un único entorno y base de datos centralizada toda esta información, de forma que se consigan mejoras posteriores heredadas de esta arquitectura, como la mejora en la consulta de información para las auditorías de acceso a datos que suelen requerirse por parte de organismos judiciales o policiales.
Con el objetivo de facilitar el cumplimiento de la normativa del SAS en cuanto auditoría se refiere, desde el área de gobernanza de la STIC se ha realizado un conjunto de componentes reutilizables que permiten a los desarrolladores incorporar todo el sistema de auditorías en sus aplicaciones de forma rápida y estandarizada, minimizando el tiempo necesario que se requiere para realizar este proceso.
Propósito y Visión
El proyecto AUDITA surge de la necesidad estratégica del Servicio Andaluz de Salud (SAS) de establecer un repositorio centralizado, único e independiente para la consolidación de registros de auditoría de negocio.
Actualmente, la dispersión de logs en sistemas asistenciales, económicos y de recursos humanos dificulta la trazabilidad y la respuesta ante requerimientos de organismos reguladores o judiciales. AUDITA resuelve este problema proporcionando una capa de persistencia unificada que garantiza la integridad, la disponibilidad y la facilidad de consulta de la actividad crítica de todos los sistemas de información del SAS.
Alcance Funcional
El sistema actúa como un hub de auditoría transversal, permitiendo la captura de eventos desde entornos heterogéneos (desde arquitecturas modernas de microservicios en contenedores hasta sistemas legacy en entornos baremetal), asegurando que cada acción de negocio quede debidamente documentada con un contexto completo (identidad, objeto, acción y resultado).
Resumen de Componentes
La solución se articula mediante una arquitectura basada en eventos y microservicios, compuesta por los siguientes pilares:
Estrategia de Consumo
Para garantizar un consumo ágil y minimizar el acoplamiento técnico entre los sistemas del SAS y la plataforma AUDITA, se han definido dos mecanismos fundamentales:
audita-connector (Capa de Aplicación) : Es la componente de tipo librería diseñada para ser integrada directamente en el proyecto. Su objetivo es facilitar la producción de eventos de negocio de forma declarativa.javax.* → jakarta.*). audita-sidecar (Capa de Transporte): Actúa como un intermediario (proxy) entre la aplicación y el núcleo de AUDITA. Este componente es totalmente autónomo, exponiendo una fachada REST que es consumida por audita-connector, y encargándose de transformar y gestionar la persistencia de la información según se configure.
DevKit
Implementación base del componente.
api)@Auditable con atributos: transformer, user (UserResolver), actionCode (enum ActionCode), auditableOnError, failIfError.AuditInvocation con acceso a className, methodName, target (objeto del método) y auditableAnnotation.AuditTransformer y UserResolver para extensión.Client con método sendAsync(AuditDataDTO, String).interceptor)AuditableInterceptor: intercepta métodos anotados con @Auditable. Captura sólo el resultado (result) del método; no captura target ni parámetros de entrada. La anotación se resuelve únicamente desde el método (method.getAnnotation).FailIfErrorStrategy, SilentStrategy, AuditAndThrownExceptionStrategy, DontAuditAndThrowStrategy.model)AuditDataDTO: campos codigoProcesoEvento, codigoAccionRealizada (tipo ActionCode), error, usuarioDispositivo, nombreCentro, nombrePrograma, correlationId, payload, objetoPadre, objetoHijo.ActionCode: valores CONSULTA, LOGIN, EDICION, CREACION, ELIMINACION.validator).rest)AuditaSidecarRestClient basado en
java.net.http.HttpClient. Constructor único con AuditSidecarRestClientConfig.CompletableFuture; sin bloqueo síncrono, sin validación del DTO antes del envío.javax.json.bind (JSON-B).Sin cambios funcionales en la API pública ni en la lógica de negocio.
@Test ausente en tres casos de prueba de AuditaSidecarRestClientTest que impedía su ejecución."code" → "codigoProcesoEvento" y "actionCode" → "codigoAccionRealizada" para reflejar el contrato real del DTO.Cambios de implementación relevantes.
api)AuditInvocation: se añaden getParameters() (Object[]) y getResult() al contrato de la interfaz. El interceptor ahora expone tanto los parámetros de entrada como el resultado al AuditTransformer.@Auditable: actionCode renombrado a action; el tipo cambia de ActionCode a Action. Ambos atributos (user y action) marcados como @Deprecated(forRemoval=true), promoviendo que la resolución se haga en el AuditTransformer.Result.java.interceptor)AuditableInterceptor: ahora captura target (instancia sobre la que se invoca) y parameters (argumentos del método) además del resultado, pasándolos a AuditInvocationDefault.java.util.logging.Logger en lugar de SLF4J.FailIfErrorStrategy renombrada a ClientFailIfErrorStrategy; nueva estrategia ValidationFailIfErrorStrategy para tratar fallos de validación del DTO de forma diferenciada.onFailure en todas las estrategias se extiende para recibir target, parameters y result.model)ActionCode renombrado a Action; valores renombrados a español técnico: CREAR, CONSULTAR, ACTUALIZAR, ELIMINAR (se elimina LOGIN).validator con cadena de responsabilidad Chain of Responsibility: AuditDataDTOValidator, AgentValidator, EntityParentObjectValidator, EntityChildObjectValidator, NullValidator, ValidationHandler.AuditaNotValidException para señalizar fallos de validación del DTO.IssueError, MessageError para representar errores de validación estructurados.AuditDataDTO añade fechaAccion y fechaRegistroAuditoria (LocalDateTime).rest)AuditaSidecarRestClient migrado de
java.net.http.HttpClient a cliente JAX-RS (
javax.ws.rs) con ClientBuilder e Invocation.Builder. Implementa AutoCloseable y gestiona su ciclo de vida con ExecutorService + ScheduledExecutorService para timeout.dto.validate()) antes del envío HTTP; un DTO inválido lanza AuditaNotValidException sin realizar llamada de red.Correcciones de compatibilidad sin cambios funcionales.
interceptor)AuditableInterceptor: prioridad fijada a valor literal 2000 en lugar de Interceptor.Priority.APPLICATION para compatibilidad con CDI 1.1 (Java EE 7).resolveAuditable(Method, Object): resuelve la anotación @Auditable contemplando proxies CDI (clase proxy, superclase, declaringClass) evitando NullPointerException en contenedores que generan proxies para beans CDI.rest)maven-enforcer-plugin reducido a 3.5.0 para compatibilidad con Maven 3.6.1 del entorno CI.@Deprecated(forRemoval=true) en los atributos de @Auditable ya que forRemoval no está soportado en JDK 8.Corrección funcional mayor: el interceptor ahora audita correctamente métodos que devuelven CompletionStage o Future.
interceptor)AuditableInterceptor: el método intercept detecta el tipo de retorno del método auditado y bifurca:CompletionStage : envuelve el stage con wrapCompletionStageWithAudit; la auditoría se emite al completarse la operación de negocio (éxito o error), no antes.Future : convertido a CompletableFuture con timeout mediante wrapFutureWithAudit y tratado como CompletionStage.ScheduledExecutorService estático (TIMEOUT_SCHEDULER, daemon) para aplicar timeout a CompletionStage en JDK 8 sin CompletableFuture.orTimeout (método no disponible hasta JDK 9). Timeout configurable mediante ASYNC_AUDIT_TIMEOUT_MINUTES (5 minutos por defecto) para evitar stages que nunca se completan.withTimeoutJava8(CompletableFuture, timeout, unit): implementación portable de timeout para JDK 8.InstanceCache, MAX_CACHE_SIZE = 10000) para Auditable, AuditTransformer y UserResolver por Method, evitando instanciación repetida y previniendo memory leak en entornos con proxies dinámicos.AuditFailureStrategyFactory extraída como campo de instancia en lugar de crearse en cada invocación.interceptor/util/InstanceCache.java.strategyFactory inicializado en el constructor (una sola vez).Ruptura de namespace javax → jakarta. Equivalente funcional a 1.1.0.1.
api)javax.* → jakarta.* (jakarta.interceptor, jakarta.enterprise, jakarta.inject).@Auditable: atributos user y action marcados @Deprecated(forRemoval=true).AuditInvocation: mismos contratos que 1.1.0.1 (getParameters(), getResult()).interceptor)AuditableInterceptor: prioridad restaurada a Interceptor.Priority.APPLICATION (disponible en CDI 2.0+/Jakarta). No incluye el método resolveAuditable de la rama 1.1.1.1; resolución de @Auditable sólo desde el método, sin soporte de proxies CDI.ClientFailIfErrorStrategy, ValidationFailIfErrorStrategy, SilentStrategy, AuditAndThrownExceptionStrategy, DontAuditAndThrowStrategy.model)Action: mismos valores que 1.1.0.1 (CREAR, CONSULTAR, ACTUALIZAR, ELIMINAR).AuditDataDTO incluye validate() con AuditDataDTOValidator y fechaAccion/fechaRegistroAuditoria.validator idéntico al de 1.1.0.1.rest)AuditaSidecarRestClient basado en java.net. http.HttpClient (JDK nativo), no en JAX-RS. Constructor principal con (config) y constructor de test con (config, HttpClient, Jsonb).jakarta.json.bind (JSON-B)..get(timeout) para propagar ClientException síncronamente al interceptor.dto.validate()).Corrección funcional en AuditInvocationDefault.
AuditableInterceptor: se añade captura de context.getTarget() además de context.getParameters(). createAuditInvocation ahora recibe y propaga target separado de parameters, corrigiendo que setTarget() recibía los parámetros en lugar del objeto invocado.Equivalente funcional a 1.1.1.1.
interceptor)AuditableInterceptor: incorporación del método resolveAuditable(Method, Object) para resolución segura de @Auditable en proxies CDI, igual que en 1.1.1.1.rest)AuditaSidecarRestClient migrado de java.net.http.HttpClient a cliente JAX-RS (jakarta.ws.rs) con ClientBuilder e Invocation.Builder, alineado con Gobierno Tecnológico SAS (WebServices REST JAX-RS). Implementa AutoCloseable con gestión de ExecutorService y ScheduledExecutorService.AuditaSidecarRestClientTest, nueva clase RestClientTest).Equivalente funcional a 1.1.2.1. Incorpora además mejoras de rendimiento y robustez en el cliente REST.
interceptor)AuditableInterceptor: mismas capacidades asíncronas que 1.1.2.1:CompletionStage, Future, síncrono) con auditoría diferida hasta completar la operación de negocio.ASYNC_AUDIT_TIMEOUT_MINUTES).InstanceCache, MAX_CACHE_SIZE = 10000) para Auditable, AuditTransformer y UserResolver.AuditFailureStrategyFactory como campo de instancia.interceptor/util/InstanceCache.java.withTimeoutJava8 ni TIMEOUT_SCHEDULER estático: usa CompletableFuture.orTimeout (disponible nativo en JDK 9+) cuando el stage es CompletableFuture.rest)AuditaSidecarRestClient: el cliente JAX-RS (jaxrsClient) se construye una sola vez en el constructor en lugar de por cada llamada sendAsync, eliminando la creación y destrucción repetida de clientes HTTP.jaxrsClient se cierra únicamente en close(), no en el bloque finally de cada petición.ThreadPoolExecutor con cola acotada (ArrayBlockingQueue, capacidad 1000) y política CallerRunsPolicy como back-pressure, en sustitución del Executors.newCachedThreadPool() sin límite que podía provocar OutOfMemoryError bajo carga extrema. Parámetros: corePoolSize=5, maxPoolSize=50, keepAlive=60s.AuditaSidecarRestClient(config, jsonb, requestExecutor, timeoutScheduler, clientBuilder) con ClientBuilder inyectable para facilitar tests sin sobrescritura de método virtual en constructor.Implementación base del componente. Equivalente funcional a Java 2.0.1.1.
SAS.Audita.Connector.Api)AuditableAttribute: atributo [Auditable] aplicable a método o clase (AttributeTargets.Method | AttributeTargets.Class, Inherited = true). Atributos: Transformer, User (marcado [Obsolete]), Action (marcado [Obsolete]), AuditableOnError, FailIfError.IAuditInvocation: interfaz con ClassName, MethodName, Target, Parameters, Result, AuditableAttribute, IsError. Contrato completo desde el inicio (equivalente a Java 1.1.0.1+).IAuditTransformer / IUserResolver: contratos de extensión para transformación de datos y resolución de usuario.IClient: contrato del cliente de envío con firma SendAsync(AuditDataDTO, string, CancellationToken).AuditInvocationDefault: implementación por defecto de IAuditInvocation.DefaultAuditTransformer / DefaultUserResolver: implementaciones por defecto.SAS.Audita.Connector.Interceptor)AuditableInterceptor: implementado como DispatchProxy (patrón de proxy dinámico de .NET). Creación mediante método estático Create<T>(target, client, logger).Invoke: detecta el tipo de retorno y bifurca:ClientException o silencia según estrategia.Task (void async): WrapTask — audita al completarse (await), con soporte de auditableOnError.Task<T> (async con resultado): WrapTaskOfT<T> — resuelve el resultado real y audita al completarse. Usa reflexión con caché para construir el método genérico en tiempo de ejecución.ResolveAuditable: busca [Auditable] en el método, tipo del target, superclase o DeclaringType. Cachea resultado por (MethodInfo, TargetType) — incluye null para métodos no anotados (ejecución sin auditoría).InstanceCache<TKey, TValue> en Util/: caché thread-safe con ConcurrentDictionary y límite configurable (MaxCacheSize = 10000) para TransformerCache, ResolverCache y WrapTaskOfTCache.AuditFailureStrategyFactory como campo de instancia, inicializada en Create.ILogger<AuditableInterceptor> inyectado (Microsoft.Extensions.Logging); soporta NullLogger si no se proporciona.ClientFailIfErrorStrategy, ValidationFailIfErrorStrategy, SilentStrategy, AuditAndThrownExceptionStrategy, DontAuditAndThrowStrategy.SAS.Audita.Connector.Model)Action (enum): valores Crear, Consultar, Actualizar, Eliminar, Ejecutar (este último no presente en Java). Incluye ActionExtensions.GetCode() y EnumExtensions.ToDescription().AuditDataDTO: campos Id, FechaAccion, FechaRegistroAuditoria, CodigoProcesoEvento, CodigoAccionRealizada (string, no enum), Error, UsuarioDispositivo, NombreCentro, NombrePrograma, CorrelationId, Payload, ObjetoPadre, ObjetoHijo. Constructor vacío público y constructor interno por builder. Método Validate().AuditDataDTOBuilder: builder fluido con métodos With*.Agent, Entity: value objects del modelo de auditoría.AuditaNotValidException, AuditaServiceException: excepciones del dominio.IssueError, MessageError: representación estructurada de errores de validación.Validator/ con Chain of Responsibility: AuditDataDTOValidator, AgentValidator, EntityParentObjectValidator, EntityChildObjectValidator, NullValidator, ValidationHandler.SAS.Audita.Connector.Rest)AuditaSidecarRestClient (sealed): basado en HttpClient inyectado (compatible con IHttpClientFactory). El HttpClient se construye externamente y se pasa por constructor — ciclo de vida gestionado por el contenedor DI del consumidor.System.Text.Json con JsonNamingPolicy.CamelCase y DateTimeConverter personalizado.dto.Validate()).ClientException por timeout (TaskCanceledException), errores de red (HttpRequestException) y respuestas con código de error HTTP.HttpClient inyectado si config.Timeout > TimeSpan.Zero; respeta timeout externo si fue configurado vía IHttpClientFactory.AuditSidecarRestClientConfig con patrón builder interno (AuditSidecarRestClientConfig.Create().WithBaseUrl(...).WithTimeout(...).Build())Implementación base del sidecar. Único modo de operación: HTTP directo a la API Audita.
application, boot, domain, http, jpa (vacío), rest.http y rest.domain)AuditDataDTO: modelo central de evento de auditoría con campos codigoProcesoEvento, codigoAccionRealizada (ActionCode), error, usuarioDispositivo, nombreCentro, nombrePrograma, correlationId, payload, objetoPadre, objetoHijo.ActionCode: enum de acciones de auditoría.AuditaRepository: interfaz única de repositorio para creación de eventos.AuditaCreateService: caso de uso de creación de auditoría.application)AuditaCreateServiceImpl: implementación mínima — recibe AuditDataDTO y delega en AuditaRepository sin resiliencia ni métricas.http)AuditDataHttpRepository: envío del evento a la API Audita mediante cliente JAX-RS (javax.ws.rs).PersistentHttpRepository: persistencia de reintentos en modo HTTP.AuditKeycloakAuthorization, AuditKeycloakEntity, KeycloakClient: obtención y caché de token OAuth2 desde Keycloak.AuditEventMapper (en http/fhirR5/mapper/): mapeo de AuditDataDTO a recurso AuditEvent FHIR R5 (HAPI FHIR).AuditEventMessageBodyWriter: serialización del AuditEvent FHIR R5 a JSON para la petición HTTP.AgentHttpEntityMapper, AuditDataHttpEntityMapper, EntityHttpEntityMapper, ResponseAuditHttpEntityMapper.rest)AuditSidecarRest: endpoint JAX-RS POST /notificaciones/auditoria/eventos — recibe AuditDataDTOCommand, mapea a dominio, invoca el caso de uso y devuelve 201 Created con Location.BusinessExceptionMapper, RuntimeExceptionMapper, ThrowableMapper: mappers de excepción a respuesta HTTP.Incremento mayor: el sidecar pasa de un único modo HTTP a una arquitectura dual HTTP/JPA seleccionable en tiempo de ejecución. Se añaden resiliencia, métricas y observabilidad.
domain)Action (nuevo): enum que reemplaza a ActionCode, con valores CREAR, CONSULTAR, ACTUALIZAR, ELIMINAR alineados con los del conector Java.ActionCode (eliminado): sustituido por Action.AuditaTransportType (nuevo): enum que define los modos de transporte HTTP y JPA.AuditDataJpaDTO (nuevo): DTO para persistencia en base de datos, con campos id, createdOn, version y referencia al AuditDataDTO de dominio.AuditoriaJpaRepository (nuevo): interfaz de repositorio para el modo JPA.AuditDataDTO: adaptado para incluir los campos necesarios en ambos modos de transporte.AgentType, EntityExtensionCode, ExtensionCode, OutcomeCode, SourceCode): movidos de http/fhirR5/dictionary/ a domain/fhir/r5/dictionary/ (desacoplamiento entre capas).AuditEventMapper (nuevo en domain/fhir/r5/mapper/): mapeo de AuditDataDTO a AuditEvent FHIR R5, movido al dominio.FhirJsonSerializer (nuevo en domain/fhir/r5/serializer/): serialización FHIR R5 a JSON, disponible en el dominio para uso por JPA.AuditaEnvConfigSource (nuevo): fuente de configuración MicroProfile desde variables de entorno.ConfigHelper (nuevo): utilidad para leer propiedades de configuración con valores por defecto tipados.AuditaSidecarParameterException (nuevo): excepción de dominio para parámetros inválidos.MessageError: ampliado con nuevos campos de detalle de error.application)AuditaCreateServiceImpl: refactorizado para soportar los dos modos de operación (AUDITA_MODE=http por defecto, AUDITA_MODE=jpa). Introducido Instance<AuditaRepository> e Instance<AuditoriaJpaRepository> para resolución CDI lazy de ambos repositorios. Añadidos @Bulkhead(value=20, waitingTaskQueue=30), @Timeout(5000), @Counted y @Timed (MicroProfile Fault Tolerance y Metrics). La operación JPA se ejecuta como @Transactional(rollbackOn=Exception.class).jpa)AuditoriaEntity (nuevo): entidad JPA mapeada a la tabla de persistencia de auditoría.AuditoriaEntityMapper (nuevo): mapeo entre AuditDataJpaDTO y AuditoriaEntity.AuditaJpaRepositoryImpl (nuevo): implementación del repositorio JPA.AuditaJpaRepositoryProducer (nuevo): productor CDI del repositorio JPA.http)WebServiceClientBuilder: actualizado para configuración dinámica del cliente JAX-RS.AuditKeycloakAuthorization: refactorización del flujo de autorización Keycloak.AuditaProducerContants: actualización de constantes de endpoint.AuditDataDTOHttpEntity, EntityHttpEntity: adaptados al nuevo modelo de dominio.HttpReponseException: ampliada con detalle de error estructurado.AgentHttpEntityMapper, AuditDataHttpEntityMapper, EntityHttpEntityMapper: adaptados al nuevo modelo.AuditDataHttpRepository, ClientProducer, HttpContext, PersistentHttpRepository: refactorizados para integración con la nueva arquitectura dual.AuditEventMapper en http/fhir/r5/mapper/ y FhirContextProducer en http/fhir/r5/handle/: renombrado de paquete (fhirR5 → fhir/r5).boot)LivenessCheck (nuevo): sonda MicroProfile Health /health/live.ReadinessCheck (nuevo): sonda MicroProfile Health /health/ready con verificación de conectividad HTTP (modo HTTP) y JNDI/SQL (modo JPA), control de @Bulkhead y ventana deslizante de rechazos.Correcciones en la deserialización del token Keycloak y en el repositorio HTTP.
http)AuditKeycloakEntity: añadidas anotaciones JSON-B (@JsonbProperty, @JsonbNillable) para mapear correctamente los campos en snake_case de la respuesta de Keycloak (access_token, expires_in, refresh_expires_in, token_type). Corregido tipo de setter de expiresIn y refreshExpiresIn de int a long (evita truncamiento silencioso en tokens con TTL elevado).KeycloakClient: ajuste de imports (orden) y corrección de log.PersistentHttpRepository: corrección en el manejo de respuestas HTTP del repositorio de persistencia.ClientProducer: ajuste en la producción del cliente JAX-RS.Primera versión del subsistema de warm-up. Resolución de problemas de configuración de unidad de persistencia externa.
boot)JndiWarmup (nuevo): bean @ApplicationScoped con @Priority(100) que en el startup (sólo modo AUDITA_MODE=jpa) resuelve el DataSource mediante JNDI y abre una conexión de prueba. Reintenta hasta AUDITA_JNDI_WARMUP_MAX_ATTEMPTS veces (5 por defecto) con espera de AUDITA_JNDI_WARMUP_DELAY_MS (1 s) entre intentos. Configurable con AUDITA_JNDI_WARMUP_ENABLED, AUDITA_JNDI_WARMUP_MAX_ATTEMPTS, AUDITA_JNDI_WARMUP_DELAY_MS, AUDITA_DATASOURCE_JNDI.ReadinessCheck: integrado con JndiWarmup. Nuevo handler para verificación de conectividad con base de datos mediante JNDI.jpa)ExternalPersistenceUnitLoader (nuevo): carga dinámicamente una unidad de persistencia externa, permitiendo que el DataSource se configure mediante JNDI en lugar de estar embebido en persistence.xml.AuditaJpaRepositoryImpl, AuditaJpaRepositoryProducer: adaptados para soportar la unidad de persistencia externa.JpaSchemaHealthChecker: comprobación del esquema JPA durante el health-check.AuditoriaEntityMapper: ajustes en el mapeo para la nueva estructura de persistencia.rest)BusinessExceptionMapper, RuntimeExceptionMapper, ThrowableMapper: revisión de los mappers de excepciones para alinear los códigos de respuesta HTTP con el contrato REST (normalización de mensajes de error y estado HTTP).BulkheadRejectionRecorder: correcciones en el registro de rechazos de @Bulkhead para la sonda de readiness.application)AuditaCreateServiceImpl: corrección en createViaHttp para propagación correcta de excepciones de negocio.Elimina la latencia de la primera petición causada por la inicialización lazy de HAPI FHIR.
boot)FhirWarmup (nuevo): bean @ApplicationScoped con @Priority(300) que durante el startup ejecuta en orden: (1) inicializa el singleton estático de FhirContext vía FhirContextProducer, (2) crea y ejercita el parser JSON de HAPI FHIR con un Patient dummy, (3) ejecuta una serialización completa con FhirJsonSerializer usando un AuditDataDTO dummy válido, (4) ejecuta el mapeo con AuditoriaEntityMapper. Si el warmup falla el arranque se aborta (@Priority obliga secuencialidad). Configurable con AUDITA_FHIR_WARMUP_ENABLED.ReadinessCheck: timeout del health-check aumentado de 5000 ms a 15 000 ms (por defecto) para acomodar el mayor tiempo de inicio con warm-ups. Métodos isSchemaValid() y canWriteAuditoria() expuestos como protected para facilitar tests. Logging de errores migrado a lambdas (() -> "...") para evitar evaluación eager de mensajes.domain)FhirContextProducer (nuevo en domain/fhir/r5/serializer/): centraliza la creación del singleton FhirContext.forR5(). El contexto FHIR se mueve de http a domain para ser reutilizable por warmups y persistencia JPA sin dependencia circular. El FhirContextProducer de http (en http/fhir/r5/handle/) es eliminado.FhirJsonSerializer: refactorizado para recibir FhirContext y AuditEventMapper como dependencias inyectadas (constructor) en lugar de instanciarlos internamente, permitiendo la reutilización del mismo contexto FHIR entre escrituras.jpa)JpaWarmup (nuevo): bean @ApplicationScoped con @Priority(200) que verifica conectividad JNDI y realiza una query trivial al esquema durante el startup en modo AUDITA_MODE=jpa, precalentando el pool de conexiones JPA antes de la primera petición real.Elimina la penalización de latencia en la primera petición productiva mediante warm-up proactivo de los proxies CDI y el stack CXF.
boot)JaxRsWarmup (nuevo): bean @ApplicationScoped con @Priority(500) que lanza un hilo en background tras el inicio del contexto CDI. El hilo espera AUDITA_JAXRS_WARMUP_DELAY_MS (3 s por defecto) para garantizar que el puerto TCP está abierto y luego ejecuta una petición HTTP dummy al endpoint de negocio, forzando la inicialización completa de Apache CXF (escaneo de clases, registro de proveedores, construcción del modelo de recursos JAX-RS). Expone un flag estático isWarmupCompleted() para ser consultado desde ReadinessCheck. Configurable con: AUDITA_JAXRS_WARMUP_ENABLED, AUDITA_JAXRS_WARMUP_DELAY_MS, AUDITA_JAXRS_WARMUP_TIMEOUT_MS.FaultToleranceWarmup (nuevo): bean @ApplicationScoped con @Priority(400) que ejecuta durante el startup una invocación dummy de AuditaCreateService.create() usando RequestContextController para activar programáticamente el contexto @RequestScoped requerido. Fuerza la inicialización de los interceptores MicroProfile Fault Tolerance (@Bulkhead, @Timeout, @Counted, @Timed) que son lazy por naturaleza. En modo AUDITA_MODE=http también invoca AuditDataHttpRepository.create() directamente. No bloqueante ante errores: un fallo de warmup registra el aviso pero no impide el arranque. Configurable con AUDITA_FT_WARMUP_ENABLED.ReadinessCheck: integrado con JaxRsWarmup.isWarmupCompleted() — el endpoint /health/ready devuelve DOWN con jaxrsWarmup=pending mientras CXF no haya completado su inicialización, evitando que el orquestador (Kubernetes) envíe tráfico antes de que el servicio esté listo.application, http, jpa)AuditaCreateServiceImpl, AuditDataHttpRepository, AuditaJpaRepositoryImpl: añadido cortocircuito (return "warmup-skipped" / return -1L) cuando codigoProcesoEvento == "WARMUP_AUDITA_SIDECAR", evitando la creación de asientos de auditoría reales durante los warm-ups de arranque.En desuso
Esta solución se mantiene documentada por motivos de compatibilidad, dado que actualmente sigue siendo utilizada por diversos aplicativos. Sin embargo, no deberá emplearse en nuevos desarrollos, que deberán integrarse mediante la solución corporativa basada en Audita.
Con el objetivo de facilitar el cumplimiento de la normativa del SAS en cuanto auditoría se refiere, desde el área de gobernanza de la STIC se ha realizado un componente reutilizable que permita a los desarrolladores incorporar todo el sistema de auditorías en sus aplicaciones de forma rápida y estandarizada, minimizando el tiempo necesario que se requiere para realizar este proceso.
Como punto de partida, y tras analizar las diferentes alternativas ya existentes, se decide partir de una librería de código abierto ya existente que cubre parcialmente las necesidades existentes en la casa, de forma que desde la STIC se ha realizado una ramificación propia de este software para ser adaptado completamente a los requerimientos de auditorías exigidos por la organización.
La librería desarrollada cuanta con las siguientes características:
audit4j-sas-audita-handler
, que simplifica la integración del componente Audit4J con Audita sin necesidad de realizar modificaciones en el código existente de la aplicación. Toda la configuración se realiza directamente en el handler mediante el fichero
audit4j.conf.yaml
.
audit4j-sas-audita-jms-consumer
. Este componente permite consumir los mensajes de la cola de mensajería y remitirlos a Audita-Sidecar, posibilitando la integración directa con Audita sin realizar modificaciones sobre el producto.
La librería original ha sido adaptada a la normativa vigente en el SAS, por lo que está diseñada para ser utilizada en proyectos que sean ejecutados bajo el paraguas de Weblogic 12.1.3. En el siguiente enlace puede consultarse el detalle de las APIs que ofrece esta versión de Weblogic: http://docs.oracle.com/middleware/1213/wls/NOTES/whatsnew.htm#BGGGHCJD
La librería desarrollada se encuentra ubicada en el artifactory corporativo del SAS, estando preparada para ser usada por cualquier usuario que lo desee. Por tanto, para hacer uso de la libería bastará con incorporar librería en pom.xml
<dependency> <groupId>es.ja.csalud.sas.componentescomunes.audit4j-extension</groupId> <artifactId>audit4j-sas</artifactId> <version>1.0.3.1</version> </dependency>
(*) Antes de hacer uso de la librería, se recomienda consultar en artifactory la última versión disponisble (http://calidad.sas.junta-andalucia.es/artifactory/sas-internal/es/ja/csalud/sas/componentescomunes/).
Una vez ha sido incorporada la librería en nuestro proyecto, es necesario realizar una pequeña configuración que permita ajustar el funcionamiento del sistema a nuestras necesidades.
El primer paso para configurar nuestro proyecto será crear un archivo YAML llamado "audit4j.conf.yaml" en el CLASSPATH del proyecto, el cual será típicamente la ruta "src/main/resources" para un proyecto tipo maven.
!Configuration # Obligatorio
# Listado de Handlers. Pueden ser uno o más.
handlers:
#- !org.audit4j.core.handler.ConsoleAuditHandler {}
#- !org.audit4j.core.handler.file.FileAuditHandler {}
#- !org.audit4j.handler.db.DatabaseAuditHandler{}
- !org.audit4j.jms.handler.JmsAuditHandler {}
# Configuración de layouts. Sólo necesario para algunos handlers, por ejemplo Console o File
# Dejar SimpleLayout si no se va a usar los handlers anteriormente indicados
layout: !org.audit4j.core.layout.SimpleLayout {}
# Configuración de la clase de la que se recupera información del usuario logado.
# La implementación por defecto DummyMetaData devuele un valores genéricos y constantes
metaData: !org.audit4j.core.DummyMetaData {}
# Comandos especiales
# -metadata: Sincronía, "sync" or "async" (Se recomienda usar "async")
# -annotationTransformer: Clase encargada de procesar las anotaciones
# + El transformer por defecto auditará todos los parámetros de entradas del método auditado
# + StrictAnnotationTransformer: Auditará SOLO los parámetros marcados con la anotación @AuditField
# + ManualAnnotationTransformer: Auditará en función de los parámetros indicados en la anotación @Audit
# o bien en lo indicado en los XML de configuración avanzada
commands:
-metadata=async
-annotationTransformer=org.audit4j.annotation.transformer.ManualAnnotationTransformer
# Propiedades adicionales
properties:
# Paquete en el se va a rastrear las anotaciones avanzadas de auditorías (no es necesario si se utilizan XMLs)
audit_fields_package: es.ja.csalud.sas.webexample
# Fichero XML del que se va a leer la información a auditar (no es necesario si se utilizan Anotaciones)
audit_fields_xml: auditConfig.xml
# Número máximo de hilos concurrentes que podrán procesar eventos de auditorías (Valor por defecto 20,
# poner un valor mayor
threads_pool_size: 20
# Nombres de la factoría y cola JMS a utilizar en caso de usar el handler JmsAuditHandler
jms_factory: ConnectionFactory-ResponsePool
jms_queue: Queue-ResponsePool
Una vez disponemos del módulo configurado, será necesario inicializar el motor al iniciar nuestro servidor. Existen diferentes formas de conseguir que un proceso se ejecute al inicializar un servidor. A modo de ejemplo, en las pruebas realizadas por el área de arquitectura se ha hecho uso de las las anotaciones @Startup y @Singleton:
@Singleton
@Startup
public class AuditConfig {
@PostConstruct
protected void init() {
// Inicialización del motor de auditoría
AuditManager.start();
// Carga del mapping configurado mediante anotaciones avanzadas o XML
AuditFieldMapperManager.start();
}
}
Una vez se inicie nuestro servidor, se mostrará por consola un mensaje similar al siguiente:
dic 01, 2016 11:14:03 AM org.audit4j.core.util.Log info INFORMACIÓN: Audit4j:INFO Audit4j initialized. Total time: 124ms dic 01, 2016 11:14:03 AM org.audit4j.core.util.Log info INFORMACIÓN: Audit4j:INFO Loading Audit field mapping
El modo más básico de utilizar el motor de auditoría es creando un objeto de tipo AuditEvent e insertarlo en la auditoría. En el siguiente ejemplo puede verse como crear el objeto e invocar al motor de auditoría. Este modo puede utilizarse en cualquier parte de nuestro código:
// Creación del evento
AuditEvent event = new AuditEvent();
event.setObject("Episodio"); // Objeto auditado
event.setAction("Cancelar"); // Acción a realizar
// Listado de campos a auditar
// Los campos con ID "nuhsa", "episodio" y "unidad" se mapearán automáticamente
// con las columnas de mismo nombre en la tabla de adutoría.
// El resto de campos se almacenarán como información adicional
event.addField(new Field("nuhsa", pIn.getNuhsa()));
event.addField(new Field("episodio", "123456"));
event.addField(new Field("unidad", "1111"));
event.addField(new Field("apellido1", pIn.getApellido1()));
// Resultado: 0 Correcto, 1 Incorrecto
event.setResultCode(new Random().nextInt() % 2);
// Datos opcionales. Si no se indican se recuperarán del MetaData configurado
event.setActor("jparriazap");
event.setActorProfile("medico");
event.setActorService("Medicina Interna");
event.setOrigin("MPA");
// Llamada DIRECTA al motor de auditoría
AuditManager.getInstance().audit(event);
La auditoría por anotaciones permiten configurar puntos de auditoría fácilmente sin necesidad de añadir código adicional a nuestros proyectos. Dentro de la auditoría por anotaciones nos encontramos con varios modos de funcionmiento de la librería, siendo el modo Manual el que se propone desde el área de gobernanza como el más indicado para las necesidades del SAS.
Es imporante destacar que por el propio funcionamiento de las anotaciones y los insterceptores de Java EE, este mecanismo funciona solamente cuando nos encontremos en un contexto CDI.
hay que añadir tambien en el bean.xml el interceptor de auditoria para que funcionen las anotaciones:
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class org.audit4j.integration.cdi.AuditInterceptor </class>
</interceptors>
</beans>
Las anotaciones disponibles son las siguientes:
@Audit: Es la anotación principal que indica que un método debe ser auditado. Permite las siguientes propiedades:
@AuditField: Es aplicable tanto a parámetros de un método como a atributos de una clase. Tiene las siguientes propiedades:
@DeIdentify: Se utiliza para anonimizar parámetros sensibles que desean ser auditados. Tiene las siguientes propiedades:
@IgnoreAudit: Indica que un parámetro de un método debe ser ignorado. Sólo tiene sentido usar esta anotación en la auditoría por defecto, donde se auditan todos los parámetros de un método menos los marcados con esta anotación.
Modo de funcionar por defecto, es el que aplica si no se indica el comando annotationTransformer en el fichero de configuración.
En este modo, se auditan todos los parámetros de entrada de un método a excepción de los marcados como @IgnoreAudit.
public class Paciente {
private long id;
private String nuhsa;
private String nombre;
@AuditIgnore
private String apellido1;
private String apellido2;
...
}
@Stateless
public class Pacientes {
@Audit (
object = "Paciente",
action = "Buscar"
)
public Paciente buscaPacienteDefault(Paciente pacienteIn, @IgnoreAudit int edad, boolean activo) {
...
}
}
En este ejemplo, todos los objetos que inyecten el servicio "Pacientes" e invoque al método "buscaPacienteStrict" generará un registro en la auditoría indicando que se ha actuado sobre el objeto "Paciente", realizando la acción "Buscar" y se auditarán todos los parámetros de entrada recursivamente (auditando también todos los atributos de los objetos complejos no marcados con @IgnoreAudit) salvo el parametro "edad" por estar marcado con la anotación @IgnoreAudit
El modo estricto audita sólo aquellos parámetros y atributos marcados a conciencia con la anotación @AuditField. En el caso de que uno de los parámetros de entradas no sea de tipo primitivo Java, el motor de auditoría recorrerá recursivamente los atributos internos y auditará sólo aquellos que estén marcados como @AuditField.
Para hacer uso de este módo es necesario indicar el comando annotationTransformer con el siguiente valor: org.audit4j.annotation.transformer.StrictAnnotationTransformer
public class Paciente {
private long id;
@AuditField(field="nuhsa")
private String nuhsa;
@AuditField(field="nombre")
private String nombre;
private String apellido1;
private String apellido2;
...
}
@Stateless
public class Pacientes {
@Audit (
object = "Paciente",
action = "Buscar"
)
public Paciente buscaPacienteStrict(@AuditField(field = "paciente") Paciente pacienteIn, @AuditField(field = "edad") int edad, boolean activo) {
...
}
}
En este ejemplo, todos los objetos que inyecten el servicio "Pacientes" e invoque al método "buscaPacienteStrict" generará un registro en la auditoría indicando que se ha actuado sobre el objeto "Paciente", realizando la acción "Buscar" y se auditarán los atributos "paciente.nuhsa", "paciente.nombre" y el segundo parámetro (edad).
El modo manual permite realizar toda la configuración a auditar directamente sobre la anotación padre @Audit o en ficheros de configuración XML. Al igual que en el modo estricto, auditará sólo aquellos parámetros y atributos marcados como auditables.
Para hacer uso de este módo es necesario indicar el comando annotationTransformer con el siguiente valor: org.audit4j.annotation.transformer.ManualAnnotationTransformer
Desde el área de gobernanza se aconseja usar este método para conseguir así un código resultante más limpio.
Dentro de este método nos encontramos dos formas utilizarlo.
Permite indicar toda la configuración de la auditoría en la misma anotación @Audit:
public class Paciente {
private long id;
private String nuhsa;
private String nombre;
private String apellido1;
private String apellido2;
...
}
@Stateless
public class Pacientes {
@Audit (
object = "Paciente",
action = "Buscar",
fields = {
@AuditField(field="nuhsa", fieldPath="[0].nuhsa"),
@AuditField(field="id", fieldPath="[0].id"),
@AuditField(field="apellido1", fieldPath="[0].apellido1"),
@AuditField(field="edad", fieldPath="[1]"),
@AuditField(field="apellido2", fieldPath="result.apellido2")
}
)
public Paciente buscaPacienteAnotacion(Paciente pacienteIn, int edad, boolean activo) {
...
}
}
En este ejemplo, todos los objetos que inyecten el servicio "Pacientes" e invoque al método "buscaPacienteAnotacion" generará un registro en la auditoría indicando que se ha actuado sobre el objeto "Paciente", realizando la acción "Buscar" y se auditarán los atributos "nuhsa", "id" y "apellido1" del primer parámetro de entrada (pacienteIn), el segundo parámetro (edad) y del objeto devuelto como resultado, el atributo "apellido2"
Permite indicar toda la configuración de la auditoría en un fichero XML, centralizando toda la configuración de auditoría de todo el proyecto completo en un sólo fichero de configuración. En este caso, a nivel de método sólo será necesario marcarlo como @Audit y no será necesario especificar más propiedades:
public class Paciente {
private long id;
private String nuhsa;
private String nombre;
private String apellido1;
private String apellido2;
...
}
@Stateless
public class Pacientes {
@Audit
public Paciente buscaPacienteXML(Paciente pacienteIn, int edad, boolean activo) {
...
}
}
<auditConfiguration> <method>...</method> <method path="es.ja.csalud.sas.webexample.service.boundary.Pacientes.buscaPacienteXML" object="Paciente" action="Buscar"> <auditField field="nuhsa" fieldPath="[0].nuhsa"/> <auditField field="id" fieldPath="[0].id"/> <auditField field="apellido1" fieldPath="[0].apellido1"/> <auditField field="edad" fieldPath="[1]"/> <auditField field="apellido2" fieldPath="result.apellido2"/> </method> <method>...</method> <method>...</method> </auditConfiguration>
En este ejemplo, todos los objetos que inyecten el servicio "Pacientes" e invoque al método "buscaPacienteXML" generará un registro en la auditoría indicando que se ha actuado sobre el objeto "Paciente", realizando la acción "Buscar" y se auditarán los atributos "nuhsa", "id" y "apellido1" del primer parámetro de entrada (pacienteIn), el segundo parámetro (edad) y del objeto devuelto como resultado, el atributo "apellido2"
Como puede verse en el apartado "¿Qué se debe auditar en el SAS?" de la normativa de auditoría, existen una serie de campos básicos que deben ser auditados, igualmente se reserva un espacio adicional para otros campos no básicos que también deban ser auditados.
Para poder especificar qué campos corresponden con cada uno de estos campos básicos se han definido un patrón de etiquetado que debe ser seguido para el buen funcionamiento del sistema. De esta forma, se definen los siguientes elmentos (No se diferencia entre mayúsculas y minúsculas):
Mención especial requiere los campos Operador, Operador_Perfil y Operador_Unidad, ya que estos campos son normalmente recuperados de la implementación de la interfaz MetaData propia de cada producto. Pero pueden existir casos en los que la aplicación no tenga sesión (Aplicaciones REST) en donde no sea posible realizar una implementación de MetaData para recuperar datos del contexto. En estos casos esta información deberá ser configurada manualmente en cada evento a auditar.
Por orden de prioridades, el motor de auditoría mirará en primer lugar si se ha configurado un valor específico para un evento, en caso de no existir lo buscará en la implementación de la clase MetaData y en caso de no existir insertará un valor por defecto.
@Stateless
public class Pacientes {
@Audit (
object = "Paciente",
action = "Buscar",
fields = {
@AuditField(field="operador", fieldPath="[0].login"),
@AuditField(field="operador_perfil", fieldPath="[0].perfil"),
@AuditField(field="nuhsa", fieldPath="[1].nuhsa"),
@AuditField(field="id", fieldPath="[1].id"),
@AuditField(field="apellido1", fieldPath="[1].apellido1"),
@AuditField(field="apellido2", fieldPath="[1].apellido2"),
@AuditField(field="edad", fieldPath="[2]")
}
)
public Paciente buscaPaciente(Usuario user, Paciente pacienteIn, int edad, boolean activo) {
...
}
}
En este ejemplo vemos como se ha configurado para que los campos operador y operador_perfil sean cargados directamente del primer parámetro de la función auditada. En el caso de la unidad del operador, al no estar configurado en el evento, se recuperará de la implementación de MetaData que se haya realizado y en caso de no existir el registro de auditoría se creará con el valor por defecto ("Default User Service")