Subdirección de las Tecnologías de la Información y Comunicaciones

Área de Gobernanza y Calidad

Contenido


Resumen
  • Versión: v01r12
  • Fecha publicación:  
  • Entrada en vigor desde: 

Cumplimiento normativo

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

Histórico de cambios

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ónv01r12Fecha publicación

 

Fecha entrada en vigor

 

Alcance
  • Versión inicial sobre el componente para facilitar la aplicación de la norma por parte de terceros proveedores

1. Módulo corporativo de auditoría

Introducción

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:

  • Fácilmente usable: Compatible con cualquier proyecto Java EE 7, siendo suficiente con incluir la librería en el pom.xml e inicializar el motor al arrancar la aplicación.
  • Versatilidad: Permite varios modos de funcionamiento. Desde llamadas directas al motor de auditoría para registrar cualquier información al uso de anotaciones o ficheros de configuración XML.
  • Síncrono vs asíncrono: Permite funcionar en modo síncrono y asíncrono, aislando los hilos de ejecución encargados de auditar del resto de la aplicación, no afectando al rendimiento de la aplicación.
  • Alto rendimiento: El uso de buffers internos permiten recibir gran cantidad de peticiones sin afectar a la estabilidad de la aplicación.
  • Varios modos de salida: Permite configurar la auditoría para escribir en consola o fichero para trabajos de depuración o base de datos y colas JMS para funcionamientos en producción.
  • Anonimación: Permite aplicar reglas de anonimación en datos sensibles. Por ejemplo, si se desea almacenar una tarjeta de crédito se podría almacenar la cadena "*********6542"
  • Mínima configuración: La configuración necesaria para funcionar es mínima.

2. Histórico de cambios

  • Versión 1.0.2.2
    • Bug: añadida dependencia que faltaba
  • Versión 1.0.2.1
    • Bug: Corregido error al auditar parámetro de tipo listado/array/colección (Antes sólo auditaba el primer elemento del listado)
    • Bug: Corregida consulta a base de datos para comprobar si existe una tabla. Por defecto buscará la tabla auditer en el esquema del usuario. Para buscar otra tabla o otro esquema hay que setear el parametro "table_name" del handle de base de datos y especificar el nombre de la tabla buscada (Indicar [Esquema].[Tabla] si la tabla está en otro esquema).
    • Mejora: Añadido java.util.Date como tipo básico, permitiendo auditar tipos Date (Se audita en formato ISO Javascript).
  • Versión 1.0.1.1
    • Bug: Corregido error al auditar entidades con collecciones no iterables. 
    • Mejora: Optimizado proceso de recorrido de atributos en objetos complejos.
  • Versión 1.0.0.1: Versión inicial


3. Flujo de información

  • AuditManager: Es el punto de entrada al motor de auditoría, es la clase a la que hay que invocar para realizar llamadas directas al motor de auditoría e igualmente es el punto por el que se canalizan todas las auditorías que pueden realizarse con esta librería, como por ejemplo las anotaciones.
  • MetaData: Esta interfaz debe ser implementada por el la aplicación que utiliza la librería, debiendo implementar sólamente tres funciones las cuales se van a encargar de recuperar la información del usuario que está realizando la acción (actor, perfil y unidad) y una identificación de la aplicación, por ejemplo "MPA". En el caso de aplicaciones donde no hay sesión de usuario (Por ejemplo, REST), en lugar de implementar esta interfaz, se deberá indicar directamente en la configuración de cada evento a auditar de donde debe recuperarse la información de actor, perfil y unidad del operador
  • AuditOutputStream: Se trata de una interfaz que es implementada por la propia librería varias veces para realizar distintas tareas de construcción/transformación/adaptación sobre los objetos que se están auditando. Por ejemplo, una de esas implementaciones es la que lee de la sesión del usuario el login y el perfil del mismo, y otra de las implementaciones es la que se encarga de procesar las anotaciones y convertirla al formato interno de la librería.
  • Handler: Los handler son el último paso del proceso, las implementaciones de esta interfaz son las que recoger el evento de auditoría y realiza el almacenamiento. Dependiendo de la implementación el almacenamiento podrá ser consola, fichero, base de datos o colas JMS.

3. Requerimentos

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


4. Instalación

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/).

5. Configuración

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

6. Instrucciones de uso

Llamádas directas

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);

Anotaciones

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:

  • object: Necesario para especificar el objeto sobre el que se está auditando.
  • action: Acción que se está realizando sobre el objeto auditado.
  • fields: Listado de anotaciones de tipo @AuditField, donde se especifican los campos exactos a auditar.

@AuditField: Es aplicable tanto a parámetros de un método como a atributos de una clase. Tiene las siguientes propiedades:

  • field: Nombre que se le desea dar al parámetro o atributo auditado en la tabla de auditoría.
  • fieldPath: Ruta hasta el parámetro. Por ejemplo, si el tercer párametro de entrada de un método a auditar es de tipo Episodio y este tiene un atributo de tipo Paciente y este a su vez tiene un atributo con el nuhsa, para auditar el nuhsa pondríamos el siguiente path: "[2].episodio.nuhsa"

@DeIdentify: Se utiliza para anonimizar parámetros sensibles que desean ser auditados. Tiene las siguientes propiedades:

  • left: Anonimizar por la izquierda. Ejemplo: @DeIdentify(left=3) -> ***456789
  • right: Anonimizar por la derecha. Ejemplo: @DeIdentify(right=3) -> 123456***
  • fromLeft: Anonimizar todos los carácteres a partir de una posición, comenzando a contar por la izquierda. Ejemplo: @DeIdentify(fromLeft=3) -> 123******
  • fromRight: Anonimizar todos los carácteres a partir de una posición, comenzando a contar por la derecha. Ejemplo: @DeIdentify(fromRight=3) -> ******789

@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.

Default

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

Strict

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).

Manual

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.

Anotación

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"

XML

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) {
			...
		}
	}
   <?xml version="1.0" encoding="UTF-8"?>
	<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"

Campos especiales

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):

  • OPERADOR: Login del operador que realiza la acción
  • OPERADOR_PERFIL: Perfil del operador que realiza la acción
  • OPERADOR_UNIDAD: Unidad funcional del operador que realiza la acción
  • NUHSA: Identificación del usuario o paciente
  • EPISODIO: Episodio sobre el que se está realizando la acción
  • UNIDAD_PACIENTE: Unidad funcional del episodio sobre el que se realiza la acción.

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.

Ejemplo

    @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")