For #126 - Don't use BoundCodeableConcept for empty enums

This commit is contained in:
jamesagnew 2015-07-17 08:34:44 -04:00
parent 3bdf846a3d
commit ddbe79cb86
12 changed files with 286 additions and 185 deletions

View File

@ -23,8 +23,8 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
@ -75,7 +75,7 @@ public interface IResourceAuditor<T extends IResource> {
* Denotes policy-defined sensitivity for the Participant Object ID such as VIP, HIV status, mental health status or similar topics * Denotes policy-defined sensitivity for the Participant Object ID such as VIP, HIV status, mental health status or similar topics
* @return the sensitivity of this resource * @return the sensitivity of this resource
*/ */
public SecurityEventObjectSensitivityEnum getSensitivity(); public BaseCodingDt getSensitivity();
} }

View File

@ -23,9 +23,9 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.dstu.resource.AdverseReaction; import ca.uhn.fhir.model.dstu.resource.AdverseReaction;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class AdverseReactionAuditor implements IResourceAuditor<AdverseReaction> { public class AdverseReactionAuditor implements IResourceAuditor<AdverseReaction> {
@ -78,7 +78,7 @@ public class AdverseReactionAuditor implements IResourceAuditor<AdverseReaction>
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
return null; return null;
} }

View File

@ -23,9 +23,9 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class DiagnosticReportAuditor implements IResourceAuditor<DiagnosticReport> { public class DiagnosticReportAuditor implements IResourceAuditor<DiagnosticReport> {
@ -83,7 +83,7 @@ public class DiagnosticReportAuditor implements IResourceAuditor<DiagnosticRepor
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
return null; //no sensitivity indicated return null; //no sensitivity indicated
} }
} }

View File

@ -23,9 +23,9 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Encounter; import ca.uhn.fhir.model.dstu.resource.Encounter;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class EncounterAuditor implements IResourceAuditor<Encounter> { public class EncounterAuditor implements IResourceAuditor<Encounter> {
@ -96,7 +96,7 @@ public class EncounterAuditor implements IResourceAuditor<Encounter> {
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
//override this method to provide sensitivity information about the visit //override this method to provide sensitivity information about the visit
return null; return null;
} }

View File

@ -22,10 +22,10 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Medication; import ca.uhn.fhir.model.dstu.resource.Medication;
import ca.uhn.fhir.model.dstu.resource.MedicationPrescription; import ca.uhn.fhir.model.dstu.resource.MedicationPrescription;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class MedicationPrescriptionAuditor implements IResourceAuditor<MedicationPrescription> { public class MedicationPrescriptionAuditor implements IResourceAuditor<MedicationPrescription> {
@ -85,7 +85,7 @@ public class MedicationPrescriptionAuditor implements IResourceAuditor<Medicatio
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
return null; //no sensitivity indicated in MedicationPrescription return null; //no sensitivity indicated in MedicationPrescription
} }

View File

@ -22,10 +22,10 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Medication; import ca.uhn.fhir.model.dstu.resource.Medication;
import ca.uhn.fhir.model.dstu.resource.MedicationStatement; import ca.uhn.fhir.model.dstu.resource.MedicationStatement;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class MedicationStatementAuditor implements IResourceAuditor<MedicationStatement> { public class MedicationStatementAuditor implements IResourceAuditor<MedicationStatement> {
@ -83,7 +83,7 @@ public class MedicationStatementAuditor implements IResourceAuditor<MedicationSt
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
return null; //no sensitivity indicated in MedicationStatement return null; //no sensitivity indicated in MedicationStatement
} }

View File

@ -23,9 +23,9 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class ObservationAuditor implements IResourceAuditor<Observation> { public class ObservationAuditor implements IResourceAuditor<Observation> {
@ -79,7 +79,7 @@ public class ObservationAuditor implements IResourceAuditor<Observation> {
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
return null; return null;
} }

View File

@ -24,9 +24,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class PatientAuditor implements IResourceAuditor<Patient> { public class PatientAuditor implements IResourceAuditor<Patient> {
@ -93,7 +93,7 @@ public class PatientAuditor implements IResourceAuditor<Patient> {
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
return null; //override to include things like locked patient records return null; //override to include things like locked patient records
} }

View File

@ -23,10 +23,10 @@ package ca.uhn.fhir.rest.server.audit;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Procedure; import ca.uhn.fhir.model.dstu.resource.Procedure;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum;
import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum;
public class ProcedureAuditor implements IResourceAuditor<Procedure> { public class ProcedureAuditor implements IResourceAuditor<Procedure> {
@ -79,7 +79,7 @@ public class ProcedureAuditor implements IResourceAuditor<Procedure> {
} }
@Override @Override
public SecurityEventObjectSensitivityEnum getSensitivity() { public BaseCodingDt getSensitivity() {
return null; return null;
} }

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.server.interceptor; package ca.uhn.fhir.rest.server.interceptor;
/* /*
* #%L * #%L
@ -66,142 +66,147 @@ import ca.uhn.fhir.store.IAuditDataStore;
/** /**
* Server interceptor that provides auditing capability * Server interceptor that provides auditing capability
* *
* To use, set a data store that implements the IAuditDataStore interface, specify * To use, set a data store that implements the IAuditDataStore interface, specify
*/ */
public class AuditingInterceptor extends InterceptorAdapter { public class AuditingInterceptor extends InterceptorAdapter {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AuditingInterceptor.class); private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AuditingInterceptor.class);
private IAuditDataStore myDataStore = null; private IAuditDataStore myDataStore = null;
private Map<String, Class<? extends IResourceAuditor<? extends IResource>>> myAuditableResources = new HashMap<String, Class<? extends IResourceAuditor<? extends IResource>>>(); private Map<String, Class<? extends IResourceAuditor<? extends IResource>>> myAuditableResources = new HashMap<String, Class<? extends IResourceAuditor<? extends IResource>>>();
private boolean myClientParamsOptional = false; private boolean myClientParamsOptional = false;
protected String mySiteId; protected String mySiteId;
/** /**
* Create a new AuditingInterceptor with auditing required by default and a siteid of "NONE" * Create a new AuditingInterceptor with auditing required by default and a siteid of "NONE"
*/ */
public AuditingInterceptor() { public AuditingInterceptor() {
mySiteId = "NONE"; mySiteId = "NONE";
myClientParamsOptional = false; myClientParamsOptional = false;
} }
/** /**
* Create a new AuditingInterceptor with auditing required by default and a site ID as specified * Create a new AuditingInterceptor with auditing required by default and a site ID as specified
* @param theSiteId the name of the site producing the audit data (will be set as SecurityEvent.Source.siteid) *
* @param theSiteId
* the name of the site producing the audit data (will be set as SecurityEvent.Source.siteid)
*/ */
public AuditingInterceptor(String theSiteId) { public AuditingInterceptor(String theSiteId) {
mySiteId = theSiteId; mySiteId = theSiteId;
myClientParamsOptional = false; myClientParamsOptional = false;
} }
/** /**
* Create a new AuditingInterceptor with the specified site ID and indicating if auditing is optional or required * Create a new AuditingInterceptor with the specified site ID and indicating if auditing is optional or required
* @param theSiteId theSiteId the name of the site producing the audit data (will be set as SecurityEvent.Source.siteid) *
* @param theClientParamsOptional - true if auditing is optional, false if auditing is required (will throw exception if * @param theSiteId
* user params are not specified or audit store is not set) * theSiteId the name of the site producing the audit data (will be set as SecurityEvent.Source.siteid)
* @param theClientParamsOptional
* - true if auditing is optional, false if auditing is required (will throw exception if user params are
* not specified or audit store is not set)
*/ */
public AuditingInterceptor(String theSiteId, boolean theClientParamsOptional){ public AuditingInterceptor(String theSiteId, boolean theClientParamsOptional) {
mySiteId = theSiteId; mySiteId = theSiteId;
myClientParamsOptional = theClientParamsOptional; myClientParamsOptional = theClientParamsOptional;
} }
/** /**
* Intercept the outgoing response to perform auditing of the request data if the bundle contains auditable resources. * Intercept the outgoing response to perform auditing of the request data if the bundle contains auditable
* resources.
*/ */
@Override @Override
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
throws AuthenticationException { if (myClientParamsOptional && myDataStore == null) {
if(myClientParamsOptional && myDataStore == null){ // auditing is not required or configured, so do nothing here
//auditing is not required or configured, so do nothing here
log.debug("No auditing configured."); log.debug("No auditing configured.");
return true; return true;
} }
if(theResponseObject == null || theResponseObject.isEmpty()){ if (theResponseObject == null || theResponseObject.isEmpty()) {
log.debug("No bundle to audit"); log.debug("No bundle to audit");
return true; return true;
} }
try{ try {
log.info("Auditing bundle: " + theResponseObject + " from request " + theRequestDetails); log.info("Auditing bundle: " + theResponseObject + " from request " + theRequestDetails);
SecurityEvent auditEvent = new SecurityEvent(); SecurityEvent auditEvent = new SecurityEvent();
auditEvent.setEvent(getEventInfo(theRequestDetails)); auditEvent.setEvent(getEventInfo(theRequestDetails));
//get user info from request if available // get user info from request if available
Participant participant = getParticipant(theServletRequest); Participant participant = getParticipant(theServletRequest);
if(participant == null){ if (participant == null) {
log.debug("No participant to audit"); log.debug("No participant to audit");
return true; //no user to audit - throws exception if client params are required return true; // no user to audit - throws exception if client params are required
} }
List<Participant> participants = new ArrayList<SecurityEvent.Participant>(1); List<Participant> participants = new ArrayList<SecurityEvent.Participant>(1);
participants.add(participant); participants.add(participant);
auditEvent.setParticipant(participants); auditEvent.setParticipant(participants);
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType()); SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
byte[] query = getQueryFromRequestDetails(theRequestDetails); byte[] query = getQueryFromRequestDetails(theRequestDetails);
List<ObjectElement> auditableObjects = new ArrayList<SecurityEvent.ObjectElement>(); List<ObjectElement> auditableObjects = new ArrayList<SecurityEvent.ObjectElement>();
for(BundleEntry entry: theResponseObject.getEntries()){ for (BundleEntry entry : theResponseObject.getEntries()) {
IResource resource = entry.getResource(); IResource resource = entry.getResource();
ObjectElement auditableObject = getObjectElement(resource, lifecycle , query); ObjectElement auditableObject = getObjectElement(resource, lifecycle, query);
if(auditableObject != null) auditableObjects.add(auditableObject); if (auditableObject != null)
auditableObjects.add(auditableObject);
} }
if(auditableObjects.isEmpty()){ if (auditableObjects.isEmpty()) {
log.debug("No auditable resources to audit."); log.debug("No auditable resources to audit.");
return true; //no PHI to audit return true; // no PHI to audit
}else{ } else {
log.debug("Auditing " + auditableObjects.size() + " resources."); log.debug("Auditing " + auditableObjects.size() + " resources.");
} }
auditEvent.setObject(auditableObjects); auditEvent.setObject(auditableObjects);
auditEvent.setSource(getSourceElement(theServletRequest)); auditEvent.setSource(getSourceElement(theServletRequest));
store(auditEvent); store(auditEvent);
return true; //success return true; // success
}catch(Exception e){ } catch (Exception e) {
log.error("Unable to audit resource: " + theResponseObject + " from request: " + theRequestDetails, e); log.error("Unable to audit resource: " + theResponseObject + " from request: " + theRequestDetails, e);
throw new InternalErrorException("Auditing failed, unable to complete request", e); throw new InternalErrorException("Auditing failed, unable to complete request", e);
} }
} }
/** /**
* Intercept the outgoing response to perform auditing of the request data if the resource is auditable. * Intercept the outgoing response to perform auditing of the request data if the resource is auditable.
*/ */
@Override @Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
throws AuthenticationException { if (myClientParamsOptional && myDataStore == null) {
if(myClientParamsOptional && myDataStore == null){ // auditing is not required or configured, so do nothing here
//auditing is not required or configured, so do nothing here
log.debug("No auditing configured."); log.debug("No auditing configured.");
return true; return true;
} }
if(theResponseObject == null){ if (theResponseObject == null) {
log.debug("No object to audit"); log.debug("No object to audit");
return true; return true;
} }
try{ try {
log.info("Auditing resource: " + theResponseObject + " from request: " + theRequestDetails); log.info("Auditing resource: " + theResponseObject + " from request: " + theRequestDetails);
SecurityEvent auditEvent = new SecurityEvent(); SecurityEvent auditEvent = new SecurityEvent();
auditEvent.setEvent(getEventInfo(theRequestDetails)); auditEvent.setEvent(getEventInfo(theRequestDetails));
//get user info from request if available // get user info from request if available
Participant participant = getParticipant(theServletRequest); Participant participant = getParticipant(theServletRequest);
if(participant == null){ if (participant == null) {
log.debug("No participant to audit"); log.debug("No participant to audit");
return true; //no user to audit - throws exception if client params are required return true; // no user to audit - throws exception if client params are required
} }
List<Participant> participants = new ArrayList<SecurityEvent.Participant>(1); List<Participant> participants = new ArrayList<SecurityEvent.Participant>(1);
participants.add(participant); participants.add(participant);
auditEvent.setParticipant(participants); auditEvent.setParticipant(participants);
byte[] query = getQueryFromRequestDetails(theRequestDetails); byte[] query = getQueryFromRequestDetails(theRequestDetails);
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType()); SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle , query); ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle, query);
if(auditableObject == null){ if (auditableObject == null) {
log.debug("No auditable resources to audit"); log.debug("No auditable resources to audit");
return true; return true;
} }
List<ObjectElement> auditableObjects = new ArrayList<SecurityEvent.ObjectElement>(1); List<ObjectElement> auditableObjects = new ArrayList<SecurityEvent.ObjectElement>(1);
auditableObjects.add(auditableObject); auditableObjects.add(auditableObject);
auditEvent.setObject(auditableObjects); auditEvent.setObject(auditableObjects);
auditEvent.setSource(getSourceElement(theServletRequest)); auditEvent.setSource(getSourceElement(theServletRequest));
log.debug("Auditing one resource."); log.debug("Auditing one resource.");
store(auditEvent); store(auditEvent);
return true; return true;
}catch(Exception e){ } catch (Exception e) {
log.error("Unable to audit resource: " + theResponseObject + " from request: " + theRequestDetails, e); log.error("Unable to audit resource: " + theResponseObject + " from request: " + theRequestDetails, e);
throw new InternalErrorException("Auditing failed, unable to complete request", e); throw new InternalErrorException("Auditing failed, unable to complete request", e);
} }
@ -209,31 +214,41 @@ public class AuditingInterceptor extends InterceptorAdapter {
/** /**
* Store the SecurityEvent generated for this request to a persistent store (database, file, JMS, etc) * Store the SecurityEvent generated for this request to a persistent store (database, file, JMS, etc)
* @param auditEvent the SecurityEvent generated for this request *
* @throws Exception if the data store is not configured or if an error occurs during storage * @param auditEvent
* the SecurityEvent generated for this request
* @throws Exception
* if the data store is not configured or if an error occurs during storage
*/ */
protected void store(SecurityEvent auditEvent) throws Exception { protected void store(SecurityEvent auditEvent) throws Exception {
if(myDataStore == null) throw new InternalErrorException("No data store provided to persist audit events"); if (myDataStore == null)
throw new InternalErrorException("No data store provided to persist audit events");
myDataStore.store(auditEvent); myDataStore.store(auditEvent);
} }
/** /**
* Generates the Event segment of the SecurityEvent based on the incoming request details * Generates the Event segment of the SecurityEvent based on the incoming request details
* @param theRequestDetails the RequestDetails of the incoming request *
* @param theRequestDetails
* the RequestDetails of the incoming request
* @return an Event populated with the action, date, and outcome * @return an Event populated with the action, date, and outcome
*/ */
protected Event getEventInfo(RequestDetails theRequestDetails) { protected Event getEventInfo(RequestDetails theRequestDetails) {
Event event = new Event(); Event event = new Event();
event.setAction(mapResourceTypeToSecurityEventAction(theRequestDetails.getResourceOperationType())); event.setAction(mapResourceTypeToSecurityEventAction(theRequestDetails.getResourceOperationType()));
event.setDateTimeWithMillisPrecision(new Date()); event.setDateTimeWithMillisPrecision(new Date());
event.setOutcome(SecurityEventOutcomeEnum.SUCCESS); //we audit successful return of PHI only, otherwise an exception is thrown and no resources are returned to be audited event.setOutcome(SecurityEventOutcomeEnum.SUCCESS); // we audit successful return of PHI only, otherwise an
// exception is thrown and no resources are returned to be
// audited
return event; return event;
} }
/** /**
* Return the query URL encoded to bytes to be set as the Object.query on the Security Event * Return the query URL encoded to bytes to be set as the Object.query on the Security Event
* @param theRequestDetails the RequestDetails of the incoming request *
* @param theRequestDetails
* the RequestDetails of the incoming request
* @return the query URL of the request encoded to bytes * @return the query URL of the request encoded to bytes
*/ */
protected byte[] getQueryFromRequestDetails(RequestDetails theRequestDetails) { protected byte[] getQueryFromRequestDetails(RequestDetails theRequestDetails) {
@ -249,22 +264,28 @@ public class AuditingInterceptor extends InterceptorAdapter {
/** /**
* If the resource is considered an auditable resource containing PHI, create an ObjectElement, otherwise return null * If the resource is considered an auditable resource containing PHI, create an ObjectElement, otherwise return null
* @param resource the resource to be audited *
* @param lifecycle the SecurityEventObjectLifecycleEnum of the request * @param resource
* @param query the byte encoded query string of the request * the resource to be audited
* @param lifecycle
* the SecurityEventObjectLifecycleEnum of the request
* @param query
* the byte encoded query string of the request
* @return an ObjectElement populated with information about the resource * @return an ObjectElement populated with information about the resource
* @throws IllegalAccessException if the auditor for this resource cannot be instantiated * @throws IllegalAccessException
* @throws InstantiationException if the auditor for this resource cannot be instantiated * if the auditor for this resource cannot be instantiated
* @throws InstantiationException
* if the auditor for this resource cannot be instantiated
*/ */
protected ObjectElement getObjectElement(IResource resource, SecurityEventObjectLifecycleEnum lifecycle, byte[] query) throws InstantiationException, IllegalAccessException { protected ObjectElement getObjectElement(IResource resource, SecurityEventObjectLifecycleEnum lifecycle, byte[] query) throws InstantiationException, IllegalAccessException {
String resourceType = resource.getResourceName(); String resourceType = resource.getResourceName();
if(myAuditableResources.containsKey(resourceType)){ if (myAuditableResources.containsKey(resourceType)) {
log.debug("Found auditable resource of type: " + resourceType); log.debug("Found auditable resource of type: " + resourceType);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
IResourceAuditor<IResource> auditableResource = (IResourceAuditor<IResource>) myAuditableResources.get(resourceType).newInstance(); IResourceAuditor<IResource> auditableResource = (IResourceAuditor<IResource>) myAuditableResources.get(resourceType).newInstance();
auditableResource.setResource(resource); auditableResource.setResource(resource);
if(auditableResource.isAuditable()){ if (auditableResource.isAuditable()) {
ObjectElement object = new ObjectElement(); ObjectElement object = new ObjectElement();
object.setReference(new ResourceReferenceDt(resource.getId())); object.setReference(new ResourceReferenceDt(resource.getId()));
object.setLifecycle(lifecycle); object.setLifecycle(lifecycle);
@ -274,198 +295,260 @@ public class AuditingInterceptor extends InterceptorAdapter {
object.setType(auditableResource.getType()); object.setType(auditableResource.getType());
object.setDescription(auditableResource.getDescription()); object.setDescription(auditableResource.getDescription());
Map<String, String> detailMap = auditableResource.getDetail(); Map<String, String> detailMap = auditableResource.getDetail();
if(detailMap != null && !detailMap.isEmpty()){ if (detailMap != null && !detailMap.isEmpty()) {
List<ObjectDetail> details = new ArrayList<SecurityEvent.ObjectDetail>(); List<ObjectDetail> details = new ArrayList<SecurityEvent.ObjectDetail>();
for(Entry<String, String> entry: detailMap.entrySet()){ for (Entry<String, String> entry : detailMap.entrySet()) {
ObjectDetail detail = makeObjectDetail(entry.getKey(), entry.getValue()); ObjectDetail detail = makeObjectDetail(entry.getKey(), entry.getValue());
details.add(detail); details.add(detail);
} }
object.setDetail(details); object.setDetail(details);
} }
object.setSensitivity(auditableResource.getSensitivity());
return object; if (auditableResource.getSensitivity() != null) {
}else{ CodingDt coding = object.getSensitivity().addCoding();
coding.setSystem(auditableResource.getSensitivity().getSystemElement().getValue());
coding.setCode(auditableResource.getSensitivity().getCodeElement().getValue());
coding.setDisplay(auditableResource.getSensitivity().getDisplayElement().getValue());
}
return object;
} else {
log.debug("Resource is not auditable"); log.debug("Resource is not auditable");
} }
}else{ } else {
log.debug("No auditor configured for resource type " + resourceType); log.debug("No auditor configured for resource type " + resourceType);
} }
return null; //not something we care to audit return null; // not something we care to audit
} }
/** /**
* Helper method to create an ObjectDetail from a pair of Strings. * Helper method to create an ObjectDetail from a pair of Strings.
* @param type the type of the ObjectDetail *
* @param value the value of the ObejctDetail to be encoded * @param type
* the type of the ObjectDetail
* @param value
* the value of the ObejctDetail to be encoded
* @return an ObjectDetail of the given type and value * @return an ObjectDetail of the given type and value
*/ */
protected ObjectDetail makeObjectDetail(String type, String value) { protected ObjectDetail makeObjectDetail(String type, String value) {
ObjectDetail detail = new ObjectDetail(); ObjectDetail detail = new ObjectDetail();
if(type != null) if (type != null)
detail.setType(type); detail.setType(type);
if(value != null) if (value != null)
detail.setValue(value.getBytes()); detail.setValue(value.getBytes());
return detail; return detail;
} }
/** /**
* Create a participant object based on information from the incoming servlet request * Create a participant object based on information from the incoming servlet request
* @param theServletRequest the incoming request *
* @param theServletRequest
* the incoming request
* @return a Participant object populated with the user id, user name, and IP * @return a Participant object populated with the user id, user name, and IP
* @throws InvalidRequestException if auditing is required but userId is not specified as a request header * @throws InvalidRequestException
* @throws NotImplementedException if the authorization type is OAuth * if auditing is required but userId is not specified as a request header
* @throws NotImplementedException
* if the authorization type is OAuth
*/ */
protected Participant getParticipant(HttpServletRequest theServletRequest) throws InvalidRequestException, NotImplementedException { protected Participant getParticipant(HttpServletRequest theServletRequest) throws InvalidRequestException, NotImplementedException {
//TODO: move the specification of auth params into another interceptor separate from auditing? // TODO: move the specification of auth params into another interceptor separate from auditing?
if(theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")){ if (theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")) {
if(myClientParamsOptional){ if (myClientParamsOptional) {
log.debug("OAuth request received but no auditing required."); log.debug("OAuth request received but no auditing required.");
return null; return null;
} }
//TODO: get user info from token // TODO: get user info from token
throw new NotImplementedException("OAuth user auditing not yet implemented."); throw new NotImplementedException("OAuth user auditing not yet implemented.");
}else { //no auth or basic auth or anything else, use HTTP headers for user info } else { // no auth or basic auth or anything else, use HTTP headers for user info
String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID); String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID);
if(userId == null){ if (userId == null) {
if(myClientParamsOptional) return null; //no auditing if (myClientParamsOptional)
else throw new InvalidRequestException(UserInfoInterceptor.HEADER_USER_ID + " must be specified as an HTTP header to access PHI."); return null; // no auditing
else
throw new InvalidRequestException(UserInfoInterceptor.HEADER_USER_ID + " must be specified as an HTTP header to access PHI.");
} }
String userName = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_NAME); String userName = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_NAME);
if(userName == null) userName = "Anonymous"; if (userName == null)
String userIp = theServletRequest.getRemoteAddr(); userName = "Anonymous";
String userIp = theServletRequest.getRemoteAddr();
Participant participant = new Participant(); Participant participant = new Participant();
participant.setUserId(userId); participant.setUserId(userId);
participant.setName(userName); participant.setName(userName);
ParticipantNetwork network = participant.getNetwork(); ParticipantNetwork network = participant.getNetwork();
network.setType(SecurityEventParticipantNetworkTypeEnum.IP_ADDRESS); network.setType(SecurityEventParticipantNetworkTypeEnum.IP_ADDRESS);
network.setIdentifier(userIp); network.setIdentifier(userIp);
return participant; return participant;
} }
} }
/** /**
* Create a Source object based on information in the incoming request * Create a Source object based on information in the incoming request
* @param theServletRequest the incoming request *
* @param theServletRequest
* the incoming request
* @return a Source object with the identifier, type, and site specified * @return a Source object with the identifier, type, and site specified
* @throws NotImplementedException if the authorization type is OAuth * @throws NotImplementedException
* if the authorization type is OAuth
*/ */
protected Source getSourceElement(HttpServletRequest theServletRequest) throws NotImplementedException{ protected Source getSourceElement(HttpServletRequest theServletRequest) throws NotImplementedException {
if(theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")){ if (theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")) {
if(myClientParamsOptional) return null; //no auditing required if (myClientParamsOptional)
//TODO: get application info from token return null; // no auditing required
// TODO: get application info from token
throw new NotImplementedException("OAuth auditing not yet implemented."); throw new NotImplementedException("OAuth auditing not yet implemented.");
}else { //no auth or basic auth or anything else, use HTTP headers for audit info } else { // no auth or basic auth or anything else, use HTTP headers for audit info
String appId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_APPLICATION_NAME); String appId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_APPLICATION_NAME);
Source source = new Source(); Source source = new Source();
source.setIdentifier(appId); source.setIdentifier(appId);
source.setType(getAccessType(theServletRequest)); source.setType(getAccessType(theServletRequest));
source.setSite(getSiteId(theServletRequest)); source.setSite(getSiteId(theServletRequest));
return source; return source;
} }
} }
/** /**
* Returns the site ID (for SecurityEvent.source.siteid) * Returns the site ID (for SecurityEvent.source.siteid)
* @param theServletRequest the incoming request *
* @return the site ID to audit. By default, uses the value set in the constructor. Override this method to set the value via request parameters. * @param theServletRequest
* the incoming request
* @return the site ID to audit. By default, uses the value set in the constructor. Override this method to set the
* value via request parameters.
*/ */
protected StringDt getSiteId(HttpServletRequest theServletRequest) { protected StringDt getSiteId(HttpServletRequest theServletRequest) {
return new StringDt(mySiteId); //override this method to pull the site id from the request info return new StringDt(mySiteId); // override this method to pull the site id from the request info
} }
/** /**
* Return the access type for this request * Return the access type for this request
* @param theServletRequest the incoming request *
* @param theServletRequest
* the incoming request
* @return the SecurityEventSourceTypeEnum representing the type of request being made * @return the SecurityEventSourceTypeEnum representing the type of request being made
*/ */
protected List<CodingDt> getAccessType(HttpServletRequest theServletRequest) { protected List<CodingDt> getAccessType(HttpServletRequest theServletRequest) {
List<CodingDt> types = new ArrayList<CodingDt>(); List<CodingDt> types = new ArrayList<CodingDt>();
if(theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")){ if (theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")) {
types.add(new CodingDt(SecurityEventSourceTypeEnum.USER_DEVICE.getSystem(), SecurityEventSourceTypeEnum.USER_DEVICE.getCode())); types.add(new CodingDt(SecurityEventSourceTypeEnum.USER_DEVICE.getSystem(), SecurityEventSourceTypeEnum.USER_DEVICE.getCode()));
}else{ } else {
String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID); String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID);
String appId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_APPLICATION_NAME); String appId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_APPLICATION_NAME);
if (userId == null && appId != null) types.add(new CodingDt(SecurityEventSourceTypeEnum.APPLICATION_SERVER.getSystem(), SecurityEventSourceTypeEnum.APPLICATION_SERVER.getCode())); if (userId == null && appId != null)
else types.add(new CodingDt(SecurityEventSourceTypeEnum.USER_DEVICE.getSystem(), SecurityEventSourceTypeEnum.USER_DEVICE.getCode())); types.add(new CodingDt(SecurityEventSourceTypeEnum.APPLICATION_SERVER.getSystem(), SecurityEventSourceTypeEnum.APPLICATION_SERVER.getCode()));
else
types.add(new CodingDt(SecurityEventSourceTypeEnum.USER_DEVICE.getSystem(), SecurityEventSourceTypeEnum.USER_DEVICE.getCode()));
} }
return types; return types;
} }
/** /**
* Returns the SecurityEventActionEnum corresponding to the specified RestfulOperationTypeEnum * Returns the SecurityEventActionEnum corresponding to the specified RestfulOperationTypeEnum
* @param resourceOperationType the type of operation (Read, Create, Delete, etc) *
* @param resourceOperationType
* the type of operation (Read, Create, Delete, etc)
* @return the corresponding SecurityEventActionEnum (Read/View/Print, Create, Delete, etc) * @return the corresponding SecurityEventActionEnum (Read/View/Print, Create, Delete, etc)
*/ */
protected SecurityEventActionEnum mapResourceTypeToSecurityEventAction(RestfulOperationTypeEnum resourceOperationType) { protected SecurityEventActionEnum mapResourceTypeToSecurityEventAction(RestfulOperationTypeEnum resourceOperationType) {
if(resourceOperationType == null) return null; if (resourceOperationType == null)
return null;
switch (resourceOperationType) { switch (resourceOperationType) {
case READ: return SecurityEventActionEnum.READ_VIEW_PRINT; case READ:
case CREATE: return SecurityEventActionEnum.CREATE; return SecurityEventActionEnum.READ_VIEW_PRINT;
case DELETE: return SecurityEventActionEnum.DELETE; case CREATE:
case HISTORY_INSTANCE: return SecurityEventActionEnum.READ_VIEW_PRINT; return SecurityEventActionEnum.CREATE;
case HISTORY_TYPE: return SecurityEventActionEnum.READ_VIEW_PRINT; case DELETE:
case SEARCH_TYPE: return SecurityEventActionEnum.READ_VIEW_PRINT; return SecurityEventActionEnum.DELETE;
case UPDATE: return SecurityEventActionEnum.UPDATE; case HISTORY_INSTANCE:
case VALIDATE: return SecurityEventActionEnum.READ_VIEW_PRINT; return SecurityEventActionEnum.READ_VIEW_PRINT;
case VREAD: return SecurityEventActionEnum.READ_VIEW_PRINT; case HISTORY_TYPE:
return SecurityEventActionEnum.READ_VIEW_PRINT;
case SEARCH_TYPE:
return SecurityEventActionEnum.READ_VIEW_PRINT;
case UPDATE:
return SecurityEventActionEnum.UPDATE;
case VALIDATE:
return SecurityEventActionEnum.READ_VIEW_PRINT;
case VREAD:
return SecurityEventActionEnum.READ_VIEW_PRINT;
default: default:
return SecurityEventActionEnum.READ_VIEW_PRINT; //read/view catch all return SecurityEventActionEnum.READ_VIEW_PRINT; // read/view catch all
} }
} }
/** /**
* Returns the SecurityEventObjectLifecycleEnum corresponding to the specified RestfulOperationTypeEnum * Returns the SecurityEventObjectLifecycleEnum corresponding to the specified RestfulOperationTypeEnum
* @param resourceOperationType the type of operation (Read, Create, Delete, etc) *
* @return the corresponding SecurityEventObjectLifecycleEnum (Access/Use, Origination/Creation, Logical Deletion, etc) * @param resourceOperationType
* the type of operation (Read, Create, Delete, etc)
* @return the corresponding SecurityEventObjectLifecycleEnum (Access/Use, Origination/Creation, Logical Deletion,
* etc)
*/ */
protected SecurityEventObjectLifecycleEnum mapResourceTypeToSecurityLifecycle(RestfulOperationTypeEnum resourceOperationType) { protected SecurityEventObjectLifecycleEnum mapResourceTypeToSecurityLifecycle(RestfulOperationTypeEnum resourceOperationType) {
if(resourceOperationType == null) return null; if (resourceOperationType == null)
return null;
switch (resourceOperationType) { switch (resourceOperationType) {
case READ: return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; case READ:
case CREATE: return SecurityEventObjectLifecycleEnum.ORIGINATION_OR_CREATION; return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE;
case DELETE: return SecurityEventObjectLifecycleEnum.LOGICAL_DELETION; case CREATE:
case HISTORY_INSTANCE: return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; return SecurityEventObjectLifecycleEnum.ORIGINATION_OR_CREATION;
case HISTORY_TYPE: return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; case DELETE:
case SEARCH_TYPE: return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; return SecurityEventObjectLifecycleEnum.LOGICAL_DELETION;
case UPDATE: return SecurityEventObjectLifecycleEnum.AMENDMENT; case HISTORY_INSTANCE:
case VALIDATE: return SecurityEventObjectLifecycleEnum.VERIFICATION; return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE;
case VREAD: return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; case HISTORY_TYPE:
return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE;
case SEARCH_TYPE:
return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE;
case UPDATE:
return SecurityEventObjectLifecycleEnum.AMENDMENT;
case VALIDATE:
return SecurityEventObjectLifecycleEnum.VERIFICATION;
case VREAD:
return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE;
default: default:
return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; //access/use catch all return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; // access/use catch all
} }
} }
/** /**
* Provide a persistent store implementing IAuditDataStore to store the audit events * Provide a persistent store implementing IAuditDataStore to store the audit events
* @param theDataStore an implementation of IAuditDataStore to be used to store the audit events *
* @param theDataStore
* an implementation of IAuditDataStore to be used to store the audit events
*/ */
public void setDataStore(IAuditDataStore theDataStore) { public void setDataStore(IAuditDataStore theDataStore) {
myDataStore = theDataStore; myDataStore = theDataStore;
} }
/** /**
* Get this auditor's map of auditable resources and auditors * Get this auditor's map of auditable resources and auditors
*
* @return the map of auditable resource types to auditors * @return the map of auditable resource types to auditors
*/ */
public Map<String, Class<? extends IResourceAuditor<? extends IResource>>> getAuditableResources() { public Map<String, Class<? extends IResourceAuditor<? extends IResource>>> getAuditableResources() {
return myAuditableResources; return myAuditableResources;
} }
/** /**
* Specify which types of resources are to be audited (Patient, Encounter, etc) and provide an IResourceAuditor for each type * Specify which types of resources are to be audited (Patient, Encounter, etc) and provide an IResourceAuditor for
* @param theAuditableResources a map of resources to be audited and their corresponding IResouceAuditors * each type
*
* @param theAuditableResources
* a map of resources to be audited and their corresponding IResouceAuditors
*/ */
public void setAuditableResources(Map<String, Class<? extends IResourceAuditor<? extends IResource>>> theAuditableResources) { public void setAuditableResources(Map<String, Class<? extends IResourceAuditor<? extends IResource>>> theAuditableResources) {
myAuditableResources = theAuditableResources; myAuditableResources = theAuditableResources;
} }
/** /**
* Add a type of auditable resource and its auditor to this AuditingInterceptor's resource map * Add a type of auditable resource and its auditor to this AuditingInterceptor's resource map
* @param resourceType the type of resource to be audited (Patient, Encounter, etc) *
* @param auditableResource an implementation of IResourceAuditor that can audit resources of the given type * @param resourceType
* the type of resource to be audited (Patient, Encounter, etc)
* @param auditableResource
* an implementation of IResourceAuditor that can audit resources of the given type
*/ */
public void addAuditableResource(String resourceType, Class<? extends IResourceAuditor<? extends IResource>> auditableResource){ public void addAuditableResource(String resourceType, Class<? extends IResourceAuditor<? extends IResource>> auditableResource) {
if(myAuditableResources == null) myAuditableResources = new HashMap<String, Class<? extends IResourceAuditor<? extends IResource>>>(); if (myAuditableResources == null)
myAuditableResources = new HashMap<String, Class<? extends IResourceAuditor<? extends IResource>>>();
myAuditableResources.put(resourceType, auditableResource); myAuditableResources.put(resourceType, auditableResource);
} }
} }

View File

@ -10,6 +10,7 @@ import java.io.InputStreamReader;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -108,6 +109,18 @@ public class ValueSetGenerator {
} }
} }
} }
/*
* Purge empty valuesets
*/
for (Iterator<java.util.Map.Entry<String, ValueSetTm>> iter = myValueSets.entrySet().iterator(); iter.hasNext(); ) {
java.util.Map.Entry<String, ValueSetTm> next = iter.next();
if (next.getValue().getCodes().isEmpty()) {
iter.remove();
continue;
}
}
// File[] files = new // File[] files = new
// File(myResourceValueSetFiles).listFiles((FilenameFilter) new // File(myResourceValueSetFiles).listFiles((FilenameFilter) new

View File

@ -14,6 +14,11 @@
<action type="add"> <action type="add">
SyntaxHighlightingInterceptor now also highlights OperationOutcome responses for errors/exceptions. SyntaxHighlightingInterceptor now also highlights OperationOutcome responses for errors/exceptions.
</action> </action>
<action type="fix" issue="126">
Model classes do not use BoundCodeableConcept for example bindings that do not
actually point to any codes (e.g. Observation.interpretation). Thanks
to GitHub user @steve1medix for reporting!
</action>
</release> </release>
<release version="1.1" date="2015-07-13"> <release version="1.1" date="2015-07-13">
<action type="add"> <action type="add">