diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java index 0631affde98..0d5e3094585 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/audit/IResourceAuditor.java @@ -23,8 +23,8 @@ package ca.uhn.fhir.rest.server.audit; import java.util.Map; 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.dstu.valueset.SecurityEventObjectSensitivityEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; @@ -75,7 +75,7 @@ public interface IResourceAuditor { * 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 */ - public SecurityEventObjectSensitivityEnum getSensitivity(); + public BaseCodingDt getSensitivity(); } diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/AdverseReactionAuditor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/AdverseReactionAuditor.java index 08bc5baff93..0ffa1f935b0 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/AdverseReactionAuditor.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/AdverseReactionAuditor.java @@ -23,9 +23,9 @@ package ca.uhn.fhir.rest.server.audit; import java.util.HashMap; 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.dstu.resource.AdverseReaction; -import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; public class AdverseReactionAuditor implements IResourceAuditor { @@ -78,7 +78,7 @@ public class AdverseReactionAuditor implements IResourceAuditor } @Override - public SecurityEventObjectSensitivityEnum getSensitivity() { + public BaseCodingDt getSensitivity() { return null; } diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/DiagnosticReportAuditor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/DiagnosticReportAuditor.java index 977bf00aad7..a8210936ebb 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/DiagnosticReportAuditor.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/DiagnosticReportAuditor.java @@ -23,9 +23,9 @@ package ca.uhn.fhir.rest.server.audit; import java.util.HashMap; 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.resource.DiagnosticReport; -import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; public class DiagnosticReportAuditor implements IResourceAuditor { @@ -83,7 +83,7 @@ public class DiagnosticReportAuditor implements IResourceAuditor { @@ -96,7 +96,7 @@ public class EncounterAuditor implements IResourceAuditor { } @Override - public SecurityEventObjectSensitivityEnum getSensitivity() { + public BaseCodingDt getSensitivity() { //override this method to provide sensitivity information about the visit return null; } diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/MedicationPrescriptionAuditor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/MedicationPrescriptionAuditor.java index 5d11cad265b..b37c07cf631 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/MedicationPrescriptionAuditor.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/MedicationPrescriptionAuditor.java @@ -22,10 +22,10 @@ package ca.uhn.fhir.rest.server.audit; 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.resource.Medication; import ca.uhn.fhir.model.dstu.resource.MedicationPrescription; -import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; public class MedicationPrescriptionAuditor implements IResourceAuditor { @@ -85,7 +85,7 @@ public class MedicationPrescriptionAuditor implements IResourceAuditor { @@ -83,7 +83,7 @@ public class MedicationStatementAuditor implements IResourceAuditor { @@ -79,7 +79,7 @@ public class ObservationAuditor implements IResourceAuditor { } @Override - public SecurityEventObjectSensitivityEnum getSensitivity() { + public BaseCodingDt getSensitivity() { return null; } diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/PatientAuditor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/PatientAuditor.java index 5a00af00ad9..d1e69733cc2 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/PatientAuditor.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/PatientAuditor.java @@ -24,9 +24,9 @@ import java.util.HashMap; import java.util.List; 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.resource.Patient; -import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; public class PatientAuditor implements IResourceAuditor { @@ -93,7 +93,7 @@ public class PatientAuditor implements IResourceAuditor { } @Override - public SecurityEventObjectSensitivityEnum getSensitivity() { + public BaseCodingDt getSensitivity() { return null; //override to include things like locked patient records } diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/ProcedureAuditor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/ProcedureAuditor.java index 8bddeaa0d61..78b2d5540e0 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/ProcedureAuditor.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/audit/ProcedureAuditor.java @@ -23,10 +23,10 @@ package ca.uhn.fhir.rest.server.audit; import java.util.HashMap; 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.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.resource.Procedure; -import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectSensitivityEnum; import ca.uhn.fhir.model.dstu.valueset.SecurityEventObjectTypeEnum; public class ProcedureAuditor implements IResourceAuditor { @@ -79,7 +79,7 @@ public class ProcedureAuditor implements IResourceAuditor { } @Override - public SecurityEventObjectSensitivityEnum getSensitivity() { + public BaseCodingDt getSensitivity() { return null; } diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java index c750f2617ac..49ad8eb4da3 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/interceptor/AuditingInterceptor.java @@ -1,4 +1,4 @@ - package ca.uhn.fhir.rest.server.interceptor; +package ca.uhn.fhir.rest.server.interceptor; /* * #%L @@ -66,142 +66,147 @@ import ca.uhn.fhir.store.IAuditDataStore; /** * 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 { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AuditingInterceptor.class); - - private IAuditDataStore myDataStore = null; + + private IAuditDataStore myDataStore = null; private Map>> myAuditableResources = new HashMap>>(); private boolean myClientParamsOptional = false; protected String mySiteId; - + /** * Create a new AuditingInterceptor with auditing required by default and a siteid of "NONE" */ public AuditingInterceptor() { mySiteId = "NONE"; - myClientParamsOptional = false; + myClientParamsOptional = false; } - + /** * 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) { mySiteId = theSiteId; - myClientParamsOptional = false; + myClientParamsOptional = false; } - + /** * 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 - * user params are not specified or audit store is not set) + * + * @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 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; 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 - public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) - throws AuthenticationException { - if(myClientParamsOptional && myDataStore == null){ - //auditing is not required or configured, so do nothing here + @Override + public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException { + if (myClientParamsOptional && myDataStore == null) { + // auditing is not required or configured, so do nothing here log.debug("No auditing configured."); return true; } - if(theResponseObject == null || theResponseObject.isEmpty()){ + if (theResponseObject == null || theResponseObject.isEmpty()) { log.debug("No bundle to audit"); return true; } - try{ + try { log.info("Auditing bundle: " + theResponseObject + " from request " + theRequestDetails); - SecurityEvent auditEvent = new SecurityEvent(); - auditEvent.setEvent(getEventInfo(theRequestDetails)); - //get user info from request if available + SecurityEvent auditEvent = new SecurityEvent(); + auditEvent.setEvent(getEventInfo(theRequestDetails)); + // get user info from request if available Participant participant = getParticipant(theServletRequest); - if(participant == null){ + if (participant == null) { 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 participants = new ArrayList(1); participants.add(participant); auditEvent.setParticipant(participants); - - SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType()); + + SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType()); byte[] query = getQueryFromRequestDetails(theRequestDetails); List auditableObjects = new ArrayList(); - for(BundleEntry entry: theResponseObject.getEntries()){ - IResource resource = entry.getResource(); - ObjectElement auditableObject = getObjectElement(resource, lifecycle , query); - if(auditableObject != null) auditableObjects.add(auditableObject); + for (BundleEntry entry : theResponseObject.getEntries()) { + IResource resource = entry.getResource(); + ObjectElement auditableObject = getObjectElement(resource, lifecycle, query); + if (auditableObject != null) + auditableObjects.add(auditableObject); } - if(auditableObjects.isEmpty()){ + if (auditableObjects.isEmpty()) { log.debug("No auditable resources to audit."); - return true; //no PHI to audit - }else{ + return true; // no PHI to audit + } else { log.debug("Auditing " + auditableObjects.size() + " resources."); } auditEvent.setObject(auditableObjects); auditEvent.setSource(getSourceElement(theServletRequest)); store(auditEvent); - return true; //success - }catch(Exception e){ + return true; // success + } catch (Exception e) { log.error("Unable to audit resource: " + theResponseObject + " from request: " + theRequestDetails, 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. */ @Override - public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) - throws AuthenticationException { - if(myClientParamsOptional && myDataStore == null){ - //auditing is not required or configured, so do nothing here + public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException { + if (myClientParamsOptional && myDataStore == null) { + // auditing is not required or configured, so do nothing here log.debug("No auditing configured."); return true; } - if(theResponseObject == null){ + if (theResponseObject == null) { log.debug("No object to audit"); return true; } - try{ + try { log.info("Auditing resource: " + theResponseObject + " from request: " + theRequestDetails); SecurityEvent auditEvent = new SecurityEvent(); - auditEvent.setEvent(getEventInfo(theRequestDetails)); - - //get user info from request if available + auditEvent.setEvent(getEventInfo(theRequestDetails)); + + // get user info from request if available Participant participant = getParticipant(theServletRequest); - if(participant == null){ + if (participant == null) { 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 participants = new ArrayList(1); participants.add(participant); auditEvent.setParticipant(participants); - + byte[] query = getQueryFromRequestDetails(theRequestDetails); SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType()); - ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle , query); - if(auditableObject == null){ + ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle, query); + if (auditableObject == null) { log.debug("No auditable resources to audit"); - return true; + return true; } List auditableObjects = new ArrayList(1); auditableObjects.add(auditableObject); auditEvent.setObject(auditableObjects); auditEvent.setSource(getSourceElement(theServletRequest)); log.debug("Auditing one resource."); - store(auditEvent); + store(auditEvent); return true; - }catch(Exception e){ + } catch (Exception e) { log.error("Unable to audit resource: " + theResponseObject + " from request: " + theRequestDetails, 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) - * @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 { - 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); } /** * 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 */ protected Event getEventInfo(RequestDetails theRequestDetails) { Event event = new Event(); event.setAction(mapResourceTypeToSecurityEventAction(theRequestDetails.getResourceOperationType())); 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 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 */ 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 - * @param resource the resource to be audited - * @param lifecycle the SecurityEventObjectLifecycleEnum of the request - * @param query the byte encoded query string of the request + * + * @param resource + * 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 - * @throws IllegalAccessException if the auditor for this resource cannot be instantiated - * @throws InstantiationException if the auditor for this resource cannot be instantiated + * @throws IllegalAccessException + * 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 { - String resourceType = resource.getResourceName(); - if(myAuditableResources.containsKey(resourceType)){ + String resourceType = resource.getResourceName(); + if (myAuditableResources.containsKey(resourceType)) { log.debug("Found auditable resource of type: " + resourceType); @SuppressWarnings("unchecked") - IResourceAuditor auditableResource = (IResourceAuditor) myAuditableResources.get(resourceType).newInstance(); + IResourceAuditor auditableResource = (IResourceAuditor) myAuditableResources.get(resourceType).newInstance(); auditableResource.setResource(resource); - if(auditableResource.isAuditable()){ + if (auditableResource.isAuditable()) { ObjectElement object = new ObjectElement(); object.setReference(new ResourceReferenceDt(resource.getId())); object.setLifecycle(lifecycle); @@ -274,198 +295,260 @@ public class AuditingInterceptor extends InterceptorAdapter { object.setType(auditableResource.getType()); object.setDescription(auditableResource.getDescription()); Map detailMap = auditableResource.getDetail(); - if(detailMap != null && !detailMap.isEmpty()){ + if (detailMap != null && !detailMap.isEmpty()) { List details = new ArrayList(); - for(Entry entry: detailMap.entrySet()){ + for (Entry entry : detailMap.entrySet()) { ObjectDetail detail = makeObjectDetail(entry.getKey(), entry.getValue()); details.add(detail); - } - object.setDetail(details); - } - object.setSensitivity(auditableResource.getSensitivity()); - return object; - }else{ + } + object.setDetail(details); + } + + if (auditableResource.getSensitivity() != null) { + 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"); } - }else{ + } else { 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. - * @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 */ - protected ObjectDetail makeObjectDetail(String type, String value) { + protected ObjectDetail makeObjectDetail(String type, String value) { ObjectDetail detail = new ObjectDetail(); - if(type != null) + if (type != null) detail.setType(type); - if(value != null) + if (value != null) detail.setValue(value.getBytes()); return detail; } - + /** * 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 - * @throws InvalidRequestException if auditing is required but userId is not specified as a request header - * @throws NotImplementedException if the authorization type is OAuth + * @throws InvalidRequestException + * 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 { - //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(myClientParamsOptional){ + // 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 (myClientParamsOptional) { 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."); - }else { //no auth or basic auth or anything else, use HTTP headers for user info - String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID); - if(userId == null){ - if(myClientParamsOptional) return null; //no auditing - else throw new InvalidRequestException(UserInfoInterceptor.HEADER_USER_ID + " must be specified as an HTTP header to access PHI."); + } else { // no auth or basic auth or anything else, use HTTP headers for user info + String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID); + if (userId == null) { + if (myClientParamsOptional) + 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); - if(userName == null) userName = "Anonymous"; - String userIp = theServletRequest.getRemoteAddr(); + if (userName == null) + userName = "Anonymous"; + String userIp = theServletRequest.getRemoteAddr(); Participant participant = new Participant(); - participant.setUserId(userId); - participant.setName(userName); + participant.setUserId(userId); + participant.setName(userName); ParticipantNetwork network = participant.getNetwork(); network.setType(SecurityEventParticipantNetworkTypeEnum.IP_ADDRESS); network.setIdentifier(userIp); return participant; } } - + /** * 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 - * @throws NotImplementedException if the authorization type is OAuth + * @throws NotImplementedException + * if the authorization type is OAuth */ - protected Source getSourceElement(HttpServletRequest theServletRequest) throws NotImplementedException{ - if(theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")){ - if(myClientParamsOptional) return null; //no auditing required - //TODO: get application info from token + protected Source getSourceElement(HttpServletRequest theServletRequest) throws NotImplementedException { + if (theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION) != null && theServletRequest.getHeader(Constants.HEADER_AUTHORIZATION).startsWith("OAuth")) { + if (myClientParamsOptional) + return null; // no auditing required + // TODO: get application info from token throw new NotImplementedException("OAuth auditing not yet implemented."); - }else { //no auth or basic auth or anything else, use HTTP headers for audit info - String appId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_APPLICATION_NAME); + } else { // no auth or basic auth or anything else, use HTTP headers for audit info + String appId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_APPLICATION_NAME); Source source = new Source(); source.setIdentifier(appId); source.setType(getAccessType(theServletRequest)); source.setSite(getSiteId(theServletRequest)); return source; - } + } } /** * 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) { - 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 - * @param theServletRequest the incoming request + * + * @param theServletRequest + * the incoming request * @return the SecurityEventSourceTypeEnum representing the type of request being made */ protected List getAccessType(HttpServletRequest theServletRequest) { - List types = new ArrayList(); - 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())); - }else{ - String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID); + List types = new ArrayList(); + 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())); + } else { + String userId = theServletRequest.getHeader(UserInfoInterceptor.HEADER_USER_ID); 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())); - else types.add(new CodingDt(SecurityEventSourceTypeEnum.USER_DEVICE.getSystem(), SecurityEventSourceTypeEnum.USER_DEVICE.getCode())); + if (userId == null && appId != null) + 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 - * @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) */ protected SecurityEventActionEnum mapResourceTypeToSecurityEventAction(RestfulOperationTypeEnum resourceOperationType) { - if(resourceOperationType == null) return null; + if (resourceOperationType == null) + return null; switch (resourceOperationType) { - case READ: return SecurityEventActionEnum.READ_VIEW_PRINT; - case CREATE: return SecurityEventActionEnum.CREATE; - case DELETE: return SecurityEventActionEnum.DELETE; - case HISTORY_INSTANCE: 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; + case READ: + return SecurityEventActionEnum.READ_VIEW_PRINT; + case CREATE: + return SecurityEventActionEnum.CREATE; + case DELETE: + return SecurityEventActionEnum.DELETE; + case HISTORY_INSTANCE: + 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: - 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 - * @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) { - if(resourceOperationType == null) return null; + if (resourceOperationType == null) + return null; switch (resourceOperationType) { - case READ: return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; - case CREATE: return SecurityEventObjectLifecycleEnum.ORIGINATION_OR_CREATION; - case DELETE: return SecurityEventObjectLifecycleEnum.LOGICAL_DELETION; - case HISTORY_INSTANCE: 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; + case READ: + return SecurityEventObjectLifecycleEnum.ACCESS_OR_USE; + case CREATE: + return SecurityEventObjectLifecycleEnum.ORIGINATION_OR_CREATION; + case DELETE: + return SecurityEventObjectLifecycleEnum.LOGICAL_DELETION; + case HISTORY_INSTANCE: + 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: - 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 - * @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) { myDataStore = theDataStore; } - + /** * Get this auditor's map of auditable resources and auditors + * * @return the map of auditable resource types to auditors */ public Map>> getAuditableResources() { return myAuditableResources; } - + /** - * Specify which types of resources are to be audited (Patient, Encounter, etc) and provide an IResourceAuditor for each type - * @param theAuditableResources a map of resources to be audited and their corresponding IResouceAuditors + * Specify which types of resources are to be audited (Patient, Encounter, etc) and provide an IResourceAuditor for + * each type + * + * @param theAuditableResources + * a map of resources to be audited and their corresponding IResouceAuditors */ public void setAuditableResources(Map>> theAuditableResources) { myAuditableResources = theAuditableResources; } - + /** * 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> auditableResource){ - if(myAuditableResources == null) myAuditableResources = new HashMap>>(); + public void addAuditableResource(String resourceType, Class> auditableResource) { + if (myAuditableResources == null) + myAuditableResources = new HashMap>>(); myAuditableResources.put(resourceType, auditableResource); } } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java index 836f94e76f0..35de14164a8 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java @@ -10,6 +10,7 @@ import java.io.InputStreamReader; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -108,6 +109,18 @@ public class ValueSetGenerator { } } } + + /* + * Purge empty valuesets + */ + for (Iterator> iter = myValueSets.entrySet().iterator(); iter.hasNext(); ) { + java.util.Map.Entry next = iter.next(); + if (next.getValue().getCodes().isEmpty()) { + iter.remove(); + continue; + } + } + // File[] files = new // File(myResourceValueSetFiles).listFiles((FilenameFilter) new diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 760daf8a42a..51c820713c2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -14,6 +14,11 @@ SyntaxHighlightingInterceptor now also highlights OperationOutcome responses for errors/exceptions. + + 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! +