|
|
@ -1,4 +1,4 @@
|
|
|
|
package ca.uhn.fhir.rest.server.interceptor;
|
|
|
|
package ca.uhn.fhir.rest.server.interceptor;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* #%L
|
|
|
|
* #%L
|
|
|
@ -86,7 +86,9 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
@ -95,39 +97,42 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
@ -136,22 +141,23 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -161,27 +167,26 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
* 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);
|
|
|
@ -189,8 +194,8 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -201,7 +206,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
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,64 +295,80 @@ 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());
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
return object;
|
|
|
|
}else{
|
|
|
|
} 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)
|
|
|
|
|
|
|
|
userName = "Anonymous";
|
|
|
|
String userIp = theServletRequest.getRemoteAddr();
|
|
|
|
String userIp = theServletRequest.getRemoteAddr();
|
|
|
|
Participant participant = new Participant();
|
|
|
|
Participant participant = new Participant();
|
|
|
|
participant.setUserId(userId);
|
|
|
|
participant.setUserId(userId);
|
|
|
@ -345,16 +382,20 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
@ -366,78 +407,112 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
@ -445,6 +520,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 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() {
|
|
|
@ -452,8 +528,11 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
@ -461,11 +540,15 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|