From ea3a2ad6e9ced3af4d207c52a7272c0cc0363f1a Mon Sep 17 00:00:00 2001 From: Prasanna Hegde Date: Thu, 15 Dec 2022 17:04:24 +0530 Subject: [PATCH 1/5] issue-3455 - adding resource info from location when return preference is OperationOutcome Signed-off-by: PrasannaHegde1 --- .../fhir/server/test/BundleTest.java | 215 +++++++++--------- .../fhir/server/util/RestAuditLogger.java | 64 +++++- 2 files changed, 174 insertions(+), 105 deletions(-) diff --git a/fhir-server-test/src/test/java/org/linuxforhealth/fhir/server/test/BundleTest.java b/fhir-server-test/src/test/java/org/linuxforhealth/fhir/server/test/BundleTest.java index dafa0781816..30b90788138 100644 --- a/fhir-server-test/src/test/java/org/linuxforhealth/fhir/server/test/BundleTest.java +++ b/fhir-server-test/src/test/java/org/linuxforhealth/fhir/server/test/BundleTest.java @@ -17,7 +17,6 @@ import java.io.ByteArrayInputStream; import java.time.Duration; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.UUID; @@ -46,7 +45,6 @@ import org.linuxforhealth.fhir.client.FHIRRequestHeader; import org.linuxforhealth.fhir.client.FHIRResponse; import org.linuxforhealth.fhir.config.ConfigurationService; -import org.linuxforhealth.fhir.config.FHIRConfigHelper; import org.linuxforhealth.fhir.config.FHIRConfiguration; import org.linuxforhealth.fhir.config.PropertyGroup; import org.linuxforhealth.fhir.core.FHIRMediaType; @@ -130,6 +128,7 @@ public class BundleTest extends FHIRServerTestBase { private static final String PATIENT_EXTENSION_URL = "http://my.url.domain.com/acme-healthcare/related-patient"; private static final String PREFER_HEADER_RETURN_REPRESENTATION = "return=representation"; + private static final String PREFER_HEADER_RETURN_OPERATION_OUTCOME = "return=OperationOutcome"; private static final String PREFER_HEADER_NAME = "Prefer"; private static Boolean kafkaAuditEnabled = false; @@ -987,64 +986,69 @@ public void testBatchCompartmentSearch() throws Exception { @Test(groups = { "batch" }, dependsOnMethods = { "testBatchUpdates" }) public void testBatchMixture() throws Exception { String method = "testBatchMixture"; - WebTarget target = getWebTarget(); - - // change at least one field so that the update below isn't skipped - patientB1 = patientB1.toBuilder() - .deceased(true) - .build(); - - // Perform a mixture of request types. - Bundle bundle = buildBundle(BundleType.BATCH); - // create - bundle = addRequestToBundle(null, bundle, HTTPVerb.POST, "Patient", null, - TestUtil.readLocalResource("Patient_DavidOrtiz.json")); - // update - bundle = addRequestToBundle(null, bundle, HTTPVerb.PUT, "Patient/" + patientB1.getId(), null, - patientB1); - // read - bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientB2.getId(), null, null); - // vread - bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, locationB1, null, null); - // history - bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientB1.getId() + "/_history", - null, null); - // search - bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient?family=Ortiz&_count=100", null, null); - - printBundle(method, "request", bundle); - - Entity entity = Entity.entity(bundle, FHIRMediaType.APPLICATION_FHIR_JSON); - Response response = target.request() - .header(PREFER_HEADER_NAME, PREFER_HEADER_RETURN_REPRESENTATION) - .post(entity, Response.class); - assertResponse(response, Response.Status.OK.getStatusCode()); - - Bundle responseBundle = getEntityWithExtraWork(response,method); - - assertResponseBundle(responseBundle, BundleType.BATCH_RESPONSE, 6); - assertGoodPostPutResponse(responseBundle.getEntry().get(0), Status.CREATED.getStatusCode()); - assertGoodPostPutResponse(responseBundle.getEntry().get(1), Status.OK.getStatusCode()); - assertGoodGetResponse(responseBundle.getEntry().get(2), Status.OK.getStatusCode()); - assertGoodGetResponse(responseBundle.getEntry().get(3), Status.OK.getStatusCode()); - assertGoodGetResponse(responseBundle.getEntry().get(4), Status.OK.getStatusCode()); - assertGoodGetResponse(responseBundle.getEntry().get(5), Status.OK.getStatusCode()); - - Bundle resultSet; - - // Verify the history results. - resultSet = (Bundle) responseBundle.getEntry().get(4).getResource(); - assertNotNull(resultSet); - assertTrue(resultSet.getEntry().size() > 2); - - // Verify the search results. - resultSet = (Bundle) responseBundle.getEntry().get(5).getResource(); - assertNotNull(resultSet); - assertTrue(resultSet.getEntry().size() >= 1); + + List preferredHeaders = List.of(PREFER_HEADER_RETURN_REPRESENTATION, PREFER_HEADER_RETURN_OPERATION_OUTCOME); + for (String preferredHeader : preferredHeaders) { + WebTarget target = getWebTarget(); - patientB1 = (Patient) responseBundle.getEntry().get(1).getResource(); + // change at least one field so that the update below isn't skipped + patientB1 = patientB1.toBuilder() + .deceased(true) + .build(); + // Perform a mixture of request types. + Bundle bundle = buildBundle(BundleType.BATCH); + // create + bundle = addRequestToBundle(null, bundle, HTTPVerb.POST, "Patient", null, + TestUtil.readLocalResource("Patient_DavidOrtiz.json")); + // update + bundle = addRequestToBundle(null, bundle, HTTPVerb.PUT, "Patient/" + patientB1.getId(), null, + patientB1); + // read + bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientB2.getId(), null, null); + // vread + bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, locationB1, null, null); + // history + bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientB1.getId() + "/_history", + null, null); + // search + bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient?family=Ortiz&_count=100", null, null); + + printBundle(method, "request", bundle); + + Entity entity = Entity.entity(bundle, FHIRMediaType.APPLICATION_FHIR_JSON); + Response response = target.request() + .header(PREFER_HEADER_NAME, preferredHeader) + .post(entity, Response.class); + assertResponse(response, Response.Status.OK.getStatusCode()); + + Bundle responseBundle = getEntityWithExtraWork(response,method); + + assertResponseBundle(responseBundle, BundleType.BATCH_RESPONSE, 6); + assertGoodPostPutResponse(responseBundle.getEntry().get(0), Status.CREATED.getStatusCode()); + assertGoodPostPutResponse(responseBundle.getEntry().get(1), Status.OK.getStatusCode()); + assertGoodGetResponse(responseBundle.getEntry().get(2), Status.OK.getStatusCode()); + assertGoodGetResponse(responseBundle.getEntry().get(3), Status.OK.getStatusCode()); + assertGoodGetResponse(responseBundle.getEntry().get(4), Status.OK.getStatusCode()); + assertGoodGetResponse(responseBundle.getEntry().get(5), Status.OK.getStatusCode()); + + Bundle resultSet; + + // Verify the history results. + resultSet = (Bundle) responseBundle.getEntry().get(4).getResource(); + assertNotNull(resultSet); + assertTrue(resultSet.getEntry().size() > 2); + + // Verify the search results. + resultSet = (Bundle) responseBundle.getEntry().get(5).getResource(); + assertNotNull(resultSet); + assertTrue(resultSet.getEntry().size() >= 1); + if (preferredHeader.equals(PREFER_HEADER_RETURN_REPRESENTATION)) { + patientB1 = (Patient) responseBundle.getEntry().get(1).getResource(); + } + } } + @Test(groups = { "transaction" }) public void testTransactionCreates() throws Exception { @@ -1542,51 +1546,58 @@ public void testTransactionMixture() throws Exception { if (!transactionSupported.booleanValue()) { return; } - - WebTarget target = getWebTarget(); - - // Perform a mixture of request types. - Bundle bundle = buildBundle(BundleType.TRANSACTION); - // create - bundle = addRequestToBundle(null, bundle, HTTPVerb.POST, "Patient", null, - TestUtil.readLocalResource("Patient_DavidOrtiz.json")); - // update - bundle = addRequestToBundle(null, bundle, HTTPVerb.PUT, "Patient/" + patientT1.getId(), null, - patientT1); - // read - bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientT2.getId(), null, null); - // vread - bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, locationT1, null, null); - // history -// bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientT1.getId() + "/_history", null, null); - // search -// bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient?family=Ortiz&_count=100", null, null); - - printBundle(method, "request", bundle); - - Entity entity = Entity.entity(bundle, FHIRMediaType.APPLICATION_FHIR_JSON); - Response response = target.request().post(entity, Response.class); - assertResponse(response, Response.Status.OK.getStatusCode()); - Bundle responseBundle = getEntityWithExtraWork(response,method); - assertResponseBundle(responseBundle, BundleType.TRANSACTION_RESPONSE, 4); - assertGoodPostPutResponse(responseBundle.getEntry().get(0), Status.CREATED.getStatusCode()); - assertGoodPostPutResponse(responseBundle.getEntry().get(1), Status.OK.getStatusCode()); - assertGoodGetResponse(responseBundle.getEntry().get(2), Status.OK.getStatusCode()); - assertGoodGetResponse(responseBundle.getEntry().get(3), Status.OK.getStatusCode()); -// assertGoodGetResponse(responseBundle.getEntry().get(4), Status.OK.getStatusCode()); -// assertGoodGetResponse(responseBundle.getEntry().get(5), Status.OK.getStatusCode()); - -// Bundle resultSet; - - // Verify the history results. -// resultSet = (Bundle) FHIRUtil.getResourceContainerResource(responseBundle.getEntry().get(4).getResource()); -// assertNotNull(resultSet); -// assertTrue(resultSet.getEntry().size() > 2); - - // Verify the search results. -// resultSet = (Bundle) FHIRUtil.getResourceContainerResource(responseBundle.getEntry().get(5).getResource()); -// assertNotNull(resultSet); -// assertTrue(resultSet.getEntry().size() > 3); + + List preferredHeaders = List.of(PREFER_HEADER_RETURN_REPRESENTATION, PREFER_HEADER_RETURN_OPERATION_OUTCOME); + for (String preferredHeader : preferredHeaders) { + WebTarget target = getWebTarget(); + + // Perform a mixture of request types. + Bundle bundle = buildBundle(BundleType.TRANSACTION); + // create + bundle = addRequestToBundle(null, bundle, HTTPVerb.POST, "Patient", null, + TestUtil.readLocalResource("Patient_DavidOrtiz.json")); + // update + bundle = addRequestToBundle(null, bundle, HTTPVerb.PUT, "Patient/" + patientT1.getId(), null, + patientT1); + // read + bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientT2.getId(), null, null); + // vread + bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, locationT1, null, null); + // history +// bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient/" + patientT1.getId() + "/_history", null, null); + // search +// bundle = addRequestToBundle(null, bundle, HTTPVerb.GET, "Patient?family=Ortiz&_count=100", null, null); + + printBundle(method, "request", bundle); + + Entity entity = Entity.entity(bundle, FHIRMediaType.APPLICATION_FHIR_JSON); + Response response = target.request() + .header(PREFER_HEADER_NAME, preferredHeader) + .post(entity, Response.class); + assertResponse(response, Response.Status.OK.getStatusCode()); + Bundle responseBundle = getEntityWithExtraWork(response,method); + assertResponseBundle(responseBundle, BundleType.TRANSACTION_RESPONSE, 4); + assertGoodPostPutResponse(responseBundle.getEntry().get(0), Status.CREATED.getStatusCode()); + assertGoodPostPutResponse(responseBundle.getEntry().get(1), Status.OK.getStatusCode()); + assertGoodGetResponse(responseBundle.getEntry().get(2), Status.OK.getStatusCode()); + assertGoodGetResponse(responseBundle.getEntry().get(3), Status.OK.getStatusCode()); +// assertGoodGetResponse(responseBundle.getEntry().get(4), Status.OK.getStatusCode()); +// assertGoodGetResponse(responseBundle.getEntry().get(5), Status.OK.getStatusCode()); + +// Bundle resultSet; + + // Verify the history results. +// resultSet = (Bundle) FHIRUtil.getResourceContainerResource(responseBundle.getEntry().get(4).getResource()); +// assertNotNull(resultSet); +// assertTrue(resultSet.getEntry().size() > 2); + + // Verify the search results. +// resultSet = (Bundle) FHIRUtil.getResourceContainerResource(responseBundle.getEntry().get(5).getResource()); +// assertNotNull(resultSet); +// assertTrue(resultSet.getEntry().size() > 3); + } + + } @Test(groups = { "batch" }) diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java index 0336c20bd82..cc1acc0fa34 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java @@ -33,6 +33,7 @@ import org.linuxforhealth.fhir.config.FHIRConfiguration; import org.linuxforhealth.fhir.config.FHIRRequestContext; import org.linuxforhealth.fhir.core.FHIRUtilities; +import org.linuxforhealth.fhir.core.HTTPReturnPreference; import org.linuxforhealth.fhir.core.util.handler.IPHandler; import org.linuxforhealth.fhir.model.resource.Basic; import org.linuxforhealth.fhir.model.resource.Bundle; @@ -395,7 +396,7 @@ private static void logBundleBatch(AuditLogService auditLogSvc, HttpServletReque AuditLogEntry entry = initLogEntry(AuditLogEventType.FHIR_BUNDLE); - populateAuditLogEntry(entry, request, responseEntry.getResource(), startTime, endTime, responseStatus); + populateBundleAuditLogEntry(entry, responseEntry, request, responseEntry.getResource(), startTime, endTime, responseStatus); if (requestEntry.getRequest() != null && requestEntry.getRequest().getMethod() != null) { boolean operation = requestEntry.getRequest().getUrl().getValue().contains("$") || requestEntry.getRequest().getUrl().getValue().contains("/%24"); @@ -431,7 +432,6 @@ private static void logBundleBatch(AuditLogService auditLogSvc, HttpServletReque // Only for BATCH we want to override the REQUEST URI and Status Code StringBuilder builder = new StringBuilder(); builder.append(request.getRequestURI()) - .append("/") .append(loc); entry.getContext() .setApiParameters( @@ -479,7 +479,7 @@ private static void logBundleTransaction(AuditLogService auditLogSvc, HttpServle for (Entry bundleEntry : responseBundle.getEntry()) { Bundle.Entry requestEntry = iter.next(); entry = initLogEntry(AuditLogEventType.FHIR_BUNDLE); - populateAuditLogEntry(entry, request, bundleEntry.getResource(), startTime, endTime, responseStatus); + populateBundleAuditLogEntry(entry, bundleEntry, request, bundleEntry.getResource(), startTime, endTime, responseStatus); entry.setDescription("FHIR Bundle Transaction request"); entry.getContext().setAction(selectActionForBundleEntry(requestEntry)); auditLogSvc.logEntry(entry); @@ -746,6 +746,64 @@ protected static AuditLogEntry populateAuditLogEntry(AuditLogEntry entry, HttpSe return entry; } + /** + * Populates the passed audit log entry for Bundle entry. + * This method will populate the resource id, resource type and version id from the + * Bundle response entry location when the return preference is "OperationOutcome". + * When the return preference is "representation" the populateAuditLogEntry method will + * populate the resource id, resource type and version id. + * @param entry + * The AuditLogEntry to be populated. + * @param responseEntry + * The Bundle response entry. + * @param request + * The HttpServletRequest representation of the REST request. + * @param resource + * The Resource object. + * @param startTime + * The start time of the request execution. + * @param endTime + * The end time of the request execution. + * @param responseStatus + * The response status. + * @return AuditLogEntry - an initialized audit log entry. + */ + protected static AuditLogEntry populateBundleAuditLogEntry(AuditLogEntry entry, Bundle.Entry responseEntry, HttpServletRequest request, Resource resource, + Date startTime, Date endTime, Response.Status responseStatus) { + final String METHODNAME = "populateBundleAuditLogEntry"; + log.entering(CLASSNAME, METHODNAME); + + // call populateAuditLogEntry to populate common attributes to all rest + populateAuditLogEntry(entry, request, resource, startTime, endTime, responseStatus); + + // Populate the resource id, resource type and version id from the + // Bundle response entry location when the return preference is "OperationOutcome". + if (HTTPReturnPreference.OPERATION_OUTCOME.equals(FHIRRequestContext.get().getReturnPreference())) { + if (responseEntry.getResponse() != null && responseEntry.getResponse().getLocation() != null) { + String location = responseEntry.getResponse().getLocation().getValue(); + String[] parts = location.split("/"); + String resourceType = null; + String id = null; + String versionId = null; + if (parts.length == 10) { + resourceType = parts[6]; + id = parts[7]; + versionId = parts[9]; + } else if (parts.length == 4) { + resourceType = parts[0]; + id = parts[1]; + versionId = parts[3]; + } + entry.getContext().setData(Data.builder().resourceType(resourceType).build()); + entry.getContext().getData().setId(id); + entry.getContext().getData().setVersionId(versionId); + } + } + + log.exiting(CLASSNAME, METHODNAME); + return entry; + } + /** * Builds and returns an AuditLogEntry with the minimum required fields populated. * From 6370db6d75826f26ac516c6437235fef50dbcde8 Mon Sep 17 00:00:00 2001 From: Prasanna Hegde Date: Wed, 4 Jan 2023 17:28:42 +0530 Subject: [PATCH 2/5] issue-3455 - adding audit log entry for each deleted resource Signed-off-by: PrasannaHegde1 --- .../spi/operation/FHIRResourceContext.java | 82 +++++++ .../operation/FHIRRestOperationResponse.java | 20 ++ .../fhir/server/resources/Delete.java | 4 +- .../FHIRRestInteractionVisitorPersist.java | 32 +++ .../fhir/server/util/FHIRRestHelper.java | 13 +- .../fhir/server/util/RestAuditLogger.java | 230 +++++++++++++----- 6 files changed, 314 insertions(+), 67 deletions(-) create mode 100644 fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRResourceContext.java diff --git a/fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRResourceContext.java b/fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRResourceContext.java new file mode 100644 index 00000000000..1290c775dcb --- /dev/null +++ b/fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRResourceContext.java @@ -0,0 +1,82 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.linuxforhealth.fhir.server.spi.operation; + + +/** + * This class is used to represent a Resource context information. + */ +public class FHIRResourceContext { + + private String resourceType; + + private String id; + + private String versionId; + + + + /** + * @param resourceType + * @param id + * @param versionId + */ + public FHIRResourceContext(String resourceType, String id, String versionId) { + super(); + this.resourceType = resourceType; + this.id = id; + this.versionId = versionId; + } + + + + /** + * @return the resourceType + */ + public String getResourceType() { + return resourceType; + } + + + /** + * @param resourceType the resourceType to set + */ + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + + /** + * @return the id + */ + public String getId() { + return id; + } + + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + + /** + * @return the versionId + */ + public String getVersionId() { + return versionId; + } + + + /** + * @param versionId the versionId to set + */ + public void setVersionId(String versionId) { + this.versionId = versionId; + } +} diff --git a/fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRRestOperationResponse.java b/fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRRestOperationResponse.java index 36e2411352a..90c27143b21 100644 --- a/fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRRestOperationResponse.java +++ b/fhir-server-spi/src/main/java/org/linuxforhealth/fhir/server/spi/operation/FHIRRestOperationResponse.java @@ -7,6 +7,7 @@ package org.linuxforhealth.fhir.server.spi.operation; import java.net.URI; +import java.util.List; import javax.ws.rs.core.Response; @@ -24,6 +25,7 @@ public class FHIRRestOperationResponse { private Resource prevResource; private OperationOutcome operationOutcome; private boolean deleted; + private List fhirResourceContexts; // For delete we need to return the version of the deletion marker private int versionForETag; @@ -155,4 +157,22 @@ public int getVersionForETag() { public void setVersionForETag(int versionForETag) { this.versionForETag = versionForETag; } + + + /** + * @return the fhirResourceContexts + */ + public List getFhirResourceContexts() { + return fhirResourceContexts; + } + + + /** + * @param fhirResourceContexts the fhirResourceContexts to set + */ + public void setFhirResourceContexts(List fhirResourceContexts) { + this.fhirResourceContexts = fhirResourceContexts; + } + + } \ No newline at end of file diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java index c23180e61c0..c1634e2f1aa 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java @@ -75,7 +75,7 @@ public Response delete(@PathParam("type") String type, @PathParam("id") String i try { RestAuditLogger.logDelete(httpServletRequest, ior != null ? ior.getResource() : null, - startTime, new Date(), status); + startTime, new Date(), status, ior.getFhirResourceContexts()); } catch (Exception e) { log.log(Level.SEVERE, AUDIT_LOGGING_ERR_MSG, e); } @@ -120,7 +120,7 @@ public Response conditionalDelete(@PathParam("type") String type) throws Excepti try { RestAuditLogger.logDelete(httpServletRequest, ior != null ? ior.getResource() : null, - startTime, new Date(), status); + startTime, new Date(), status, ior.getFhirResourceContexts()); } catch (Exception e) { log.log(Level.SEVERE, AUDIT_LOGGING_ERR_MSG, e); } diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/rest/FHIRRestInteractionVisitorPersist.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/rest/FHIRRestInteractionVisitorPersist.java index 01cd614eeca..76bfb2f5781 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/rest/FHIRRestInteractionVisitorPersist.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/rest/FHIRRestInteractionVisitorPersist.java @@ -8,6 +8,7 @@ import static org.linuxforhealth.fhir.model.type.String.string; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -15,6 +16,7 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response.Status; +import org.linuxforhealth.fhir.core.FHIRConstants; import org.linuxforhealth.fhir.exception.FHIROperationException; import org.linuxforhealth.fhir.model.patch.FHIRPatch; import org.linuxforhealth.fhir.model.resource.Bundle; @@ -22,6 +24,7 @@ import org.linuxforhealth.fhir.model.resource.OperationOutcome; import org.linuxforhealth.fhir.model.resource.OperationOutcome.Issue; import org.linuxforhealth.fhir.model.resource.Resource; +import org.linuxforhealth.fhir.model.type.Extension; import org.linuxforhealth.fhir.model.util.FHIRUtil; import org.linuxforhealth.fhir.persistence.SingleResourceResult; import org.linuxforhealth.fhir.persistence.context.FHIRPersistenceEvent; @@ -31,6 +34,7 @@ import org.linuxforhealth.fhir.server.exception.FHIRResourceDeletedException; import org.linuxforhealth.fhir.server.exception.FHIRResourceNotFoundException; import org.linuxforhealth.fhir.server.spi.operation.FHIROperationContext; +import org.linuxforhealth.fhir.server.spi.operation.FHIRResourceContext; import org.linuxforhealth.fhir.server.spi.operation.FHIRResourceHelpers; import org.linuxforhealth.fhir.server.spi.operation.FHIRRestOperationResponse; import org.linuxforhealth.fhir.server.util.FHIRUrlParser; @@ -48,6 +52,8 @@ public class FHIRRestInteractionVisitorPersist extends FHIRRestInteractionVisito // True if the bundle type is TRANSACTION final boolean transaction; + + private static final String EXTENSION_BASE_URL = FHIRConstants.EXT_BASE + "deletedId"; /** * Public constructor @@ -234,6 +240,7 @@ public FHIRRestOperationResponse doDelete(int entryIndex, String requestDescript .status(string(Integer.toString(httpStatus))) .outcome(oo) .build()) + .extension(buildFHIRResourceContext(ior.getFhirResourceContexts())) .build(); }); @@ -340,4 +347,29 @@ private void doInteraction(int entryIndex, String requestDescription, long accum setEntryComplete(entryIndex, entry, requestDescription, accumulatedTime + elapsed); } } + + /** + * Method to build a list of Extension objects which contains the deleted resources id, type and version Id. + * @param resourceContexts the resourceContext with deleted resource id, type and versionId. + * @return The list of Extension objects with the value in the format {resourceType}/{resourceId}/{versionId}. + */ + private List buildFHIRResourceContext(List resourceContexts) { + List extensions = new ArrayList<>(); + if (resourceContexts != null && !resourceContexts.isEmpty()) { + for (FHIRResourceContext resourceContext : resourceContexts) { + if (resourceContext.getId() != null && resourceContext.getResourceType() != null) { + Extension.Builder builder = Extension.builder(); + builder.url(EXTENSION_BASE_URL); + StringBuilder extensionValue = new StringBuilder(resourceContext.getResourceType()) + .append("/") + .append(resourceContext.getId()) + .append("/") + .append(resourceContext.getVersionId()); + builder.value(extensionValue.toString()); + extensions.add(builder.build()); + } + } + } + return extensions; + } } \ No newline at end of file diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/FHIRRestHelper.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/FHIRRestHelper.java index 130edc13abc..2d3ea4f057b 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/FHIRRestHelper.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/FHIRRestHelper.java @@ -140,6 +140,7 @@ import org.linuxforhealth.fhir.server.spi.operation.FHIROperation; import org.linuxforhealth.fhir.server.spi.operation.FHIROperationContext; import org.linuxforhealth.fhir.server.spi.operation.FHIROperationUtil; +import org.linuxforhealth.fhir.server.spi.operation.FHIRResourceContext; import org.linuxforhealth.fhir.server.spi.operation.FHIRResourceHelpers; import org.linuxforhealth.fhir.server.spi.operation.FHIRRestOperationResponse; import org.linuxforhealth.fhir.validation.FHIRValidator; @@ -1027,11 +1028,12 @@ public FHIRRestOperationResponse doDelete(String type, String id, String searchQ } if (responseBundle != null) { - + // adding a list of FHIRResourceContext which will contain the deleted resource id, type and versionId. + List resourceContexts = new ArrayList<>(); for (Entry entry: responseBundle.getEntry()) { id = entry.getResource().getId(); Resource resourceToDelete = entry.getResource(); - + // For soft-delete we store a new version of the resource with the deleted // flag set. Because we've read the resource already, we need to check that // the version we are deleting matches the version we just read, so the @@ -1070,9 +1072,14 @@ public FHIRRestOperationResponse doDelete(String type, String id, String searchQ final int newVersionNumber = currentVersionNumber + 1; Resource deletionMarker = FHIRPersistenceUtil.copyAndSetResourceMetaFields(resourceToDelete, resourceToDelete.getId(), newVersionNumber, lastUpdated); event.setFhirResource(deletionMarker); + + // Populate the FHIRResourceContext with resource type, id and versionId. This will be used to populate the audit event for each deleted resource. + resourceContexts.add(new FHIRResourceContext(type, resourceToDelete.getId(), String.valueOf(newVersionNumber))); + getInterceptorMgr().fireAfterDeleteEvent(event); } - + //set the resourceContexts to the FHIRRestOperationResponse + ior.setFhirResourceContexts(resourceContexts); warnings.add(Issue.builder() .severity(IssueSeverity.INFORMATION) .code(IssueType.INFORMATIONAL) diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java index cc1acc0fa34..45ec0447ab6 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java @@ -8,9 +8,11 @@ import java.security.Principal; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,11 +44,15 @@ import org.linuxforhealth.fhir.model.type.Code; import org.linuxforhealth.fhir.model.type.CodeableConcept; import org.linuxforhealth.fhir.model.type.Coding; +import org.linuxforhealth.fhir.model.type.Extension; import org.linuxforhealth.fhir.model.type.Id; import org.linuxforhealth.fhir.model.type.Meta; +import org.linuxforhealth.fhir.model.type.Uri; import org.linuxforhealth.fhir.model.type.code.BundleType; import org.linuxforhealth.fhir.model.type.code.HTTPVerb; import org.linuxforhealth.fhir.model.util.FHIRUtil; +import org.linuxforhealth.fhir.server.spi.operation.FHIRResourceContext; +import static org.linuxforhealth.fhir.model.util.ModelSupport.FHIR_STRING; /** * This class provides convenience methods for FHIR Rest services that need to write FHIR audit log entries. @@ -211,21 +217,33 @@ public static void logRead(HttpServletRequest request, Resource resource, Date s * The end time of the read request execution. * @param responseStatus * The response status. + * @param resourceContexts + * The resource context information. * @throws Exception */ - public static void logDelete(HttpServletRequest request, Resource resource, Date startTime, Date endTime, Response.Status responseStatus) throws Exception { + public static void logDelete(HttpServletRequest request, Resource resource, Date startTime, Date endTime, Response.Status responseStatus, List resourceContexts) throws Exception { final String METHODNAME = "logDelete"; log.entering(CLASSNAME, METHODNAME); - AuditLogService auditLogSvc = AuditLogServiceFactory.getService(); if (auditLogSvc.isEnabled()) { - AuditLogEntry entry = initLogEntry(AuditLogEventType.FHIR_DELETE); - populateAuditLogEntry(entry, request, resource, startTime, endTime, responseStatus); - - entry.getContext().setAction("D"); - entry.setDescription("FHIR Delete request"); - - auditLogSvc.logEntry(entry); + // populate and log an audit entry with the resource context information for each resource that was successfully deleted. + if (responseStatus == Status.OK && resourceContexts != null && !resourceContexts.isEmpty()) { + for (FHIRResourceContext resourceContext : resourceContexts) { + AuditLogEntry entry = initLogEntry(AuditLogEventType.FHIR_DELETE); + populateAuditLogEntry(entry, request, resource, startTime, endTime, responseStatus); + populateResourceContext(entry, resourceContext); + entry.getContext().setAction("D"); + entry.setDescription("FHIR Delete request"); + auditLogSvc.logEntry(entry); + } + } else { + // in case of failure log the + AuditLogEntry entry = initLogEntry(AuditLogEventType.FHIR_DELETE); + populateAuditLogEntry(entry, request, resource, startTime, endTime, responseStatus); + entry.getContext().setAction("D"); + entry.setDescription("FHIR Delete request"); + auditLogSvc.logEntry(entry); + } } log.exiting(CLASSNAME, METHODNAME); } @@ -393,14 +411,11 @@ private static void logBundleBatch(AuditLogService auditLogSvc, HttpServletReque Iterator iter = requestBundle.getEntry().iterator(); for (Entry responseEntry : responseBundle.getEntry()) { Bundle.Entry requestEntry = iter.next(); - - AuditLogEntry entry = initLogEntry(AuditLogEventType.FHIR_BUNDLE); - - populateBundleAuditLogEntry(entry, responseEntry, request, responseEntry.getResource(), startTime, endTime, responseStatus); + String action = "E"; if (requestEntry.getRequest() != null && requestEntry.getRequest().getMethod() != null) { boolean operation = requestEntry.getRequest().getUrl().getValue().contains("$") || requestEntry.getRequest().getUrl().getValue().contains("/%24"); - String action = "E"; + HTTPVerb requestMethod = requestEntry.getRequest().getMethod(); switch (HTTPVerb.Value.from(requestMethod.getValue())) { case GET: @@ -424,28 +439,35 @@ private static void logBundleBatch(AuditLogService auditLogSvc, HttpServletReque default: break; } - entry.getContext().setAction(action); - } + } String loc = requestEntry.getRequest().getUrl().getValue(); + // if action is delete log an audit entry for each deleted resource + if (action.equals("D") && !(responseEntry.getExtension() == null || responseEntry.getExtension().isEmpty())) { + logBundleDelete(auditLogSvc, responseEntry, request, null, startTime, endTime, responseStatus, loc, "FHIR Bundle Batch request"); + } else { + AuditLogEntry entry = initLogEntry(AuditLogEventType.FHIR_BUNDLE); + populateBundleAuditLogEntry(entry, responseEntry, request, responseEntry.getResource(), startTime, endTime, responseStatus); + + // Only for BATCH we want to override the REQUEST URI and Status Code + StringBuilder builder = new StringBuilder(); + builder.append(request.getRequestURI()) + .append(loc); + entry.getContext() + .setApiParameters( + ApiParameters.builder() + .request(builder.toString()) + .status(Integer.parseInt(responseEntry.getResponse().getStatus().getValue())) + .build()); + entry.getContext().setAction(action); + entry.setDescription("FHIR Bundle Batch request"); + + // @implNote The audit messages can be batched and sent off to the logEntry. + // The signature would be updated to AuditEntry... entries + // Then a loop and bulk action on Kafka. + auditLogSvc.logEntry(entry); + } - // Only for BATCH we want to override the REQUEST URI and Status Code - StringBuilder builder = new StringBuilder(); - builder.append(request.getRequestURI()) - .append(loc); - entry.getContext() - .setApiParameters( - ApiParameters.builder() - .request(builder.toString()) - .status(Integer.parseInt(responseEntry.getResponse().getStatus().getValue())) - .build()); - - entry.setDescription("FHIR Bundle Batch request"); - - // @implNote The audit messages can be batched and sent off to the logEntry. - // The signature would be updated to AuditEntry... entries - // Then a loop and bulk action on Kafka. - auditLogSvc.logEntry(entry); } } } @@ -478,11 +500,17 @@ private static void logBundleTransaction(AuditLogService auditLogSvc, HttpServle Iterator iter = requestBundle.getEntry().iterator(); for (Entry bundleEntry : responseBundle.getEntry()) { Bundle.Entry requestEntry = iter.next(); - entry = initLogEntry(AuditLogEventType.FHIR_BUNDLE); - populateBundleAuditLogEntry(entry, bundleEntry, request, bundleEntry.getResource(), startTime, endTime, responseStatus); - entry.setDescription("FHIR Bundle Transaction request"); - entry.getContext().setAction(selectActionForBundleEntry(requestEntry)); - auditLogSvc.logEntry(entry); + String action = selectActionForBundleEntry(requestEntry); + // if action is delete log an audit entry for each deleted resource + if (action.equals("D") && !(bundleEntry.getExtension() == null || bundleEntry.getExtension().isEmpty())) { + logBundleDelete(auditLogSvc, bundleEntry, request, null, startTime, endTime, responseStatus, null, "FHIR Bundle Transaction request"); + } else { + entry = initLogEntry(AuditLogEventType.FHIR_BUNDLE); + populateBundleAuditLogEntry(entry, bundleEntry, request, bundleEntry.getResource(), startTime, endTime, responseStatus); + entry.setDescription("FHIR Bundle Transaction request"); + entry.getContext().setAction(action); + auditLogSvc.logEntry(entry); + } } } else { // log a single audit event message when the batch transaction has failed. @@ -746,6 +774,31 @@ protected static AuditLogEntry populateAuditLogEntry(AuditLogEntry entry, HttpSe return entry; } + /** + * Populate the resource context(resource id, type and version id) into the AuditLogEntry. + * @param entry the audit log entry to be populated. + * @param resourceContext the resource context which needs to be populated into the AuditLogEntry. + * @return AuditLogEntry - an audit log entry with the required resource context populated. + */ + protected static AuditLogEntry populateResourceContext(AuditLogEntry entry, FHIRResourceContext resourceContext) { + final String METHODNAME = "populateResourceContext"; + log.entering(CLASSNAME, METHODNAME); + if (resourceContext != null) { + entry.getContext().setData(Data.builder().build()); + if (resourceContext.getResourceType() != null) { + entry.getContext().getData().setResourceType(resourceContext.getResourceType()); + } + if (resourceContext.getId() != null) { + entry.getContext().getData().setId(resourceContext.getId()); + } + if (resourceContext.getVersionId() != null) { + entry.getContext().getData().setVersionId(resourceContext.getVersionId()); + } + } + return entry; + } + + /** * Populates the passed audit log entry for Bundle entry. * This method will populate the resource id, resource type and version id from the @@ -769,40 +822,93 @@ protected static AuditLogEntry populateAuditLogEntry(AuditLogEntry entry, HttpSe * @return AuditLogEntry - an initialized audit log entry. */ protected static AuditLogEntry populateBundleAuditLogEntry(AuditLogEntry entry, Bundle.Entry responseEntry, HttpServletRequest request, Resource resource, - Date startTime, Date endTime, Response.Status responseStatus) { + Date startTime, Date endTime, Response.Status responseStatus) { final String METHODNAME = "populateBundleAuditLogEntry"; log.entering(CLASSNAME, METHODNAME); - + // call populateAuditLogEntry to populate common attributes to all rest populateAuditLogEntry(entry, request, resource, startTime, endTime, responseStatus); - + if (HTTPReturnPreference.REPRESENTATION.equals(FHIRRequestContext.get().getReturnPreference())) { + return entry; + } + if (responseEntry.getResponse() == null || responseEntry.getResponse().getLocation() == null) { + return entry; + } // Populate the resource id, resource type and version id from the // Bundle response entry location when the return preference is "OperationOutcome". - if (HTTPReturnPreference.OPERATION_OUTCOME.equals(FHIRRequestContext.get().getReturnPreference())) { - if (responseEntry.getResponse() != null && responseEntry.getResponse().getLocation() != null) { - String location = responseEntry.getResponse().getLocation().getValue(); - String[] parts = location.split("/"); - String resourceType = null; - String id = null; - String versionId = null; - if (parts.length == 10) { - resourceType = parts[6]; - id = parts[7]; - versionId = parts[9]; - } else if (parts.length == 4) { - resourceType = parts[0]; - id = parts[1]; - versionId = parts[3]; - } - entry.getContext().setData(Data.builder().resourceType(resourceType).build()); - entry.getContext().getData().setId(id); - entry.getContext().getData().setVersionId(versionId); - } + String location = responseEntry.getResponse().getLocation().getValue(); + String[] parts = location.split("/"); + if (parts.length > 3) { + Collections.reverse(Arrays.asList(parts)); + entry.getContext().setData(Data.builder().build()); + entry.getContext().getData().setResourceType(parts[3]); + entry.getContext().getData().setVersionId(parts[0]); + entry.getContext().getData().setId(parts[2]); } - log.exiting(CLASSNAME, METHODNAME); return entry; } + + /** + * Log an audit entry for each deleted resource in a Bundle.Entry + * @param auditLogSvc + * The internal FHIR Server API for audit logging. + * @param responseEntry + * The Bundle response entry. + * @param request + * The HttpServletRequest representation of the REST request. + * @param resource + * The Resource object. + * @param startTime + * The start time of the request execution. + * @param endTime + * The end time of the request execution. + * @param responseStatus + * The response status. + * @param location + * @throws Exception + */ + protected static void logBundleDelete(AuditLogService auditLogSvc, Bundle.Entry responseEntry, HttpServletRequest request, Resource resource, + Date startTime, Date endTime, Response.Status responseStatus, String location, String description) throws Exception { + final String METHODNAME = "logBundleDelete"; + log.entering(CLASSNAME, METHODNAME); + + // Populate the resource id, resource type and version id from the + // Bundle response entry extensions + for (Extension extension : responseEntry.getExtension()) { + AuditLogEntry entry = initLogEntry(AuditLogEventType.FHIR_BUNDLE); + populateAuditLogEntry(entry, request, resource, startTime, endTime, responseStatus); + String resourceInfo = extension.getValue().as(FHIR_STRING).getValue(); + String[] parts = resourceInfo.split("/"); + if (parts.length > 2) { + entry.getContext().setData(Data.builder().build()); + entry.getContext().getData().setResourceType(parts[0]); + entry.getContext().getData().setId(parts[1]); + entry.getContext().getData().setVersionId(parts[2]); + + } + if (location != null) { + // Only for BATCH we want to override the REQUEST URI and Status Code + StringBuilder builder = new StringBuilder(); + builder.append(request.getRequestURI()) + .append(location); + entry.getContext() + .setApiParameters( + ApiParameters.builder() + .request(builder.toString()) + .status(Integer.parseInt(responseEntry.getResponse().getStatus().getValue())) + .build()); + } + entry.getContext().setAction("D"); + entry.setDescription(description); + + // @implNote The audit messages can be batched and sent off to the logEntry. + // The signature would be updated to AuditEntry... entries + // Then a loop and bulk action on Kafka. + auditLogSvc.logEntry(entry); + } + log.exiting(CLASSNAME, METHODNAME); + } /** * Builds and returns an AuditLogEntry with the minimum required fields populated. From 94908736a17bff7e2d3ff51569a6e3aa9e390657 Mon Sep 17 00:00:00 2001 From: Prasanna Hegde Date: Fri, 13 Jan 2023 18:03:39 +0530 Subject: [PATCH 3/5] issue-3455 - adding null check for location Signed-off-by: PrasannaHegde1 --- .../org/linuxforhealth/fhir/server/util/RestAuditLogger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java index 45ec0447ab6..1e1cd4455ca 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java @@ -441,7 +441,7 @@ private static void logBundleBatch(AuditLogService auditLogSvc, HttpServletReque } } - String loc = requestEntry.getRequest().getUrl().getValue(); + String loc = requestEntry.getRequest().getUrl() != null ? requestEntry.getRequest().getUrl().getValue() : ""; // if action is delete log an audit entry for each deleted resource if (action.equals("D") && !(responseEntry.getExtension() == null || responseEntry.getExtension().isEmpty())) { logBundleDelete(auditLogSvc, responseEntry, request, null, startTime, endTime, responseStatus, loc, "FHIR Bundle Batch request"); From be16ef0c75358a14be3540773b4d8836c6d37277 Mon Sep 17 00:00:00 2001 From: Prasanna Hegde Date: Fri, 13 Jan 2023 18:43:07 +0530 Subject: [PATCH 4/5] issue-3455 - adding null check for location Signed-off-by: PrasannaHegde1 --- .../java/org/linuxforhealth/fhir/server/resources/Delete.java | 4 ++-- .../org/linuxforhealth/fhir/server/util/RestAuditLogger.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java index c1634e2f1aa..4128f953b54 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/resources/Delete.java @@ -75,7 +75,7 @@ public Response delete(@PathParam("type") String type, @PathParam("id") String i try { RestAuditLogger.logDelete(httpServletRequest, ior != null ? ior.getResource() : null, - startTime, new Date(), status, ior.getFhirResourceContexts()); + startTime, new Date(), status, ior != null ? ior.getFhirResourceContexts() : null); } catch (Exception e) { log.log(Level.SEVERE, AUDIT_LOGGING_ERR_MSG, e); } @@ -120,7 +120,7 @@ public Response conditionalDelete(@PathParam("type") String type) throws Excepti try { RestAuditLogger.logDelete(httpServletRequest, ior != null ? ior.getResource() : null, - startTime, new Date(), status, ior.getFhirResourceContexts()); + startTime, new Date(), status, ior != null ? ior.getFhirResourceContexts() : null); } catch (Exception e) { log.log(Level.SEVERE, AUDIT_LOGGING_ERR_MSG, e); } diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java index 1e1cd4455ca..d1eaf64a0a4 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java @@ -441,7 +441,7 @@ private static void logBundleBatch(AuditLogService auditLogSvc, HttpServletReque } } - String loc = requestEntry.getRequest().getUrl() != null ? requestEntry.getRequest().getUrl().getValue() : ""; + String loc = requestEntry.getRequest() != null && requestEntry.getRequest().getUrl() != null ? requestEntry.getRequest().getUrl().getValue() : ""; // if action is delete log an audit entry for each deleted resource if (action.equals("D") && !(responseEntry.getExtension() == null || responseEntry.getExtension().isEmpty())) { logBundleDelete(auditLogSvc, responseEntry, request, null, startTime, endTime, responseStatus, loc, "FHIR Bundle Batch request"); From 41763c95785496cb01a5acd1e47de73d96cf32f3 Mon Sep 17 00:00:00 2001 From: Prasanna Hegde Date: Mon, 23 Jan 2023 17:57:46 +0530 Subject: [PATCH 5/5] issue-3455 - fixing method indent Signed-off-by: PrasannaHegde1 --- .../org/linuxforhealth/fhir/server/util/RestAuditLogger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java index d1eaf64a0a4..8375efe8e8d 100644 --- a/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java +++ b/fhir-server/src/main/java/org/linuxforhealth/fhir/server/util/RestAuditLogger.java @@ -822,7 +822,7 @@ protected static AuditLogEntry populateResourceContext(AuditLogEntry entry, FHIR * @return AuditLogEntry - an initialized audit log entry. */ protected static AuditLogEntry populateBundleAuditLogEntry(AuditLogEntry entry, Bundle.Entry responseEntry, HttpServletRequest request, Resource resource, - Date startTime, Date endTime, Response.Status responseStatus) { + Date startTime, Date endTime, Response.Status responseStatus) { final String METHODNAME = "populateBundleAuditLogEntry"; log.entering(CLASSNAME, METHODNAME);