-
Notifications
You must be signed in to change notification settings - Fork 14
US65644: Map ACS-specific audit events to Predix-specific ones #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package com.ge.predix.acs.audit; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.context.annotation.Profile; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.ge.predix.audit.AuditEvent; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.CategoryType; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.Classifier; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.EventType; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.PublisherType; | ||
| import com.ge.predix.audit.sdk.message.AuditEventV2; | ||
| import com.ge.predix.audit.sdk.message.AuditEventV2.AuditEventV2Builder; | ||
|
|
||
| @Component | ||
| @Profile("predix") | ||
| public class PredixEventMapper { | ||
|
|
||
| private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(PredixEventProcessor.class); | ||
|
|
||
| private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); | ||
|
|
||
| public AuditEventV2 map(final AuditEvent auditEvent) { | ||
| Map<String, String> auditPayload = new HashMap<>(); | ||
| auditPayload.put("httpMethod", auditEvent.getMethod()); | ||
| auditPayload.put("requestBody", auditEvent.getRequestBody()); | ||
| auditPayload.put("responseBody", auditEvent.getResponseBody()); | ||
|
|
||
| String payload = ""; | ||
| try { | ||
| payload = OBJECT_MAPPER.writeValueAsString(auditPayload); | ||
| } catch (JsonProcessingException e) { | ||
| LOGGER.warn("Unable to convert audit payload to json: " + auditPayload.toString()); | ||
| } | ||
|
|
||
| AuditEventV2Builder auditEventBuilder = AuditEventV2.builder().timestamp(auditEvent.getTime().toEpochMilli()) | ||
| .correlationId(auditEvent.getCorrelationId()).tenantUuid(auditEvent.getZoneId()) | ||
| .publisherType(PublisherType.APP_SERVICE).categoryType(CategoryType.API_CALLS).payload(payload); | ||
|
|
||
| if (isSuccessful(auditEvent)) { | ||
| auditEventBuilder = auditEventBuilder.eventType(EventType.SUCCESS_API_REQUEST) | ||
| .classifier(Classifier.SUCCESS); | ||
| } else { | ||
| auditEventBuilder = auditEventBuilder.eventType(EventType.FAILURE_API_REQUEST) | ||
| .classifier(Classifier.FAILURE); | ||
| } | ||
|
|
||
| return auditEventBuilder.build(); | ||
| } | ||
|
|
||
| private boolean isSuccessful(final AuditEvent auditEvent) { | ||
| return HttpStatus.valueOf(auditEvent.getStatus()).is2xxSuccessful(); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package com.ge.predix.acs.audit; | ||
|
|
||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
|
|
||
| import com.ge.predix.audit.AuditEvent; | ||
| import com.ge.predix.audit.AuditEventProcessor; | ||
| import com.ge.predix.audit.sdk.AuditClient; | ||
| import com.ge.predix.audit.sdk.exception.AuditException; | ||
| import com.ge.predix.audit.sdk.message.AuditEventV2; | ||
|
|
||
| public class PredixEventProcessor implements AuditEventProcessor { | ||
|
|
||
| private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(PredixEventProcessor.class); | ||
|
|
||
| @Autowired | ||
| private AuditClient auditClient; | ||
|
|
||
| @Autowired | ||
| private PredixEventMapper eventMapper; | ||
|
|
||
| @Override | ||
| public boolean process(final AuditEvent auditEvent) { | ||
| AuditEventV2 predixEvent = eventMapper.map(auditEvent); | ||
| try { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need proper exception handling rather than catching the exception and returning boolean
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Boolean is required because of the way we created the tests for the AuditEventProcessor. Also we shouldn't add the throws at the interface level because different implementations of AuditEventProcessor may not use the Predix-Audit SDK. |
||
| this.auditClient.audit(predixEvent); | ||
| } catch (AuditException e) { | ||
| LOGGER.warn("Audit failed on process with event: " + predixEvent.toString()); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package com.ge.predix.acs.audit; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| import org.springframework.mock.web.MockHttpServletRequest; | ||
| import org.springframework.mock.web.MockHttpServletResponse; | ||
| import org.springframework.web.util.ContentCachingRequestWrapper; | ||
| import org.springframework.web.util.ContentCachingResponseWrapper; | ||
| import org.testng.Assert; | ||
| import org.testng.annotations.DataProvider; | ||
| import org.testng.annotations.Test; | ||
|
|
||
| import com.ge.predix.audit.AuditEvent; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.CategoryType; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.Classifier; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.EventType; | ||
| import com.ge.predix.audit.sdk.message.AuditEnums.PublisherType; | ||
| import com.ge.predix.audit.sdk.message.AuditEventV2; | ||
|
|
||
| public class PredixEventMapperTest { | ||
|
|
||
| // CHECKSTYLE:OFF: MagicNumber | ||
| @Test(dataProvider = "predixMapperDataProvider") | ||
| public void testPredixAuditEventMapper(final String requestBody, final String methodType, final int status, | ||
| final String responseBody, final String zoneId, final String correlationId, | ||
| final PublisherType publisherType, final CategoryType categoryType, final EventType eventType, | ||
| final Classifier classifier) throws IOException { | ||
|
|
||
| PredixEventMapper mapper = new PredixEventMapper(); | ||
|
|
||
| // setup mocked request and response | ||
| MockHttpServletRequest request = new MockHttpServletRequest(); | ||
| request.setContent(requestBody.getBytes()); | ||
| MockHttpServletResponse response = new MockHttpServletResponse(); | ||
| request.setMethod(methodType); | ||
| request.setRequestURI("not-used"); | ||
| response.setStatus(status); | ||
|
|
||
| ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(request); | ||
| ContentCachingResponseWrapper cachedResponse = new ContentCachingResponseWrapper(response); | ||
| cachedResponse.getWriter().write(responseBody); | ||
| cachedRequest.getReader().readLine(); | ||
|
|
||
| AuditEvent event = new AuditEvent(cachedRequest, cachedResponse, zoneId, correlationId); | ||
| AuditEventV2 predixEvent = mapper.map(event); | ||
|
|
||
| Assert.assertTrue(predixEvent.getPayload().contains(requestBody)); | ||
| Assert.assertTrue(predixEvent.getPayload().contains(methodType)); | ||
| Assert.assertTrue(predixEvent.getPayload().contains(responseBody)); | ||
| Assert.assertEquals(predixEvent.getPublisherType(), publisherType); | ||
| Assert.assertEquals(predixEvent.getCategoryType(), categoryType); | ||
| Assert.assertEquals(predixEvent.getEventType(), eventType); | ||
| Assert.assertEquals(predixEvent.getClassifier(), classifier); | ||
| } | ||
| // CHECKSTYLE:ON: MagicNumber | ||
|
|
||
| @DataProvider | ||
| public Object[][] predixMapperDataProvider() { | ||
| return new Object[][] { | ||
| new Object[] { "request body", "POST", 401, "response body", "1234", "5678", PublisherType.APP_SERVICE, | ||
| CategoryType.API_CALLS, EventType.FAILURE_API_REQUEST, Classifier.FAILURE }, | ||
| { "request body", "PUT", 200, "response body", "9101112", "13141516", PublisherType.APP_SERVICE, | ||
| CategoryType.API_CALLS, EventType.SUCCESS_API_REQUEST, Classifier.SUCCESS } }; | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably add at least one test for auditing input validation failures: If it's outside the scope of the current release, we should at least create a story to track this.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added story for this in audit feature
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The story for CategoryType.MALICIOUS /input validation has been added to the backlog. |
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package com.ge.predix.acs.audit; | ||
|
|
||
| import static org.mockito.Matchers.anyObject; | ||
|
|
||
| import org.mockito.InjectMocks; | ||
| import org.mockito.Mock; | ||
| import org.mockito.Mockito; | ||
| import org.mockito.MockitoAnnotations; | ||
| import org.testng.Assert; | ||
| import org.testng.annotations.BeforeMethod; | ||
| import org.testng.annotations.Test; | ||
|
|
||
| import com.ge.predix.audit.AuditEvent; | ||
| import com.ge.predix.audit.sdk.AuditClient; | ||
| import com.ge.predix.audit.sdk.exception.AuditException; | ||
| import com.ge.predix.audit.sdk.message.AuditEventV2; | ||
| import com.ge.predix.eventhub.EventHubClientException; | ||
|
|
||
| public class PredixEventProcessorTest { | ||
|
|
||
| @InjectMocks | ||
| private PredixEventProcessor eventProcessor; | ||
|
|
||
| @Mock | ||
| private AuditClient mockedClient; | ||
|
|
||
| @Mock | ||
| private PredixEventMapper mockedMapper; | ||
|
|
||
| @BeforeMethod | ||
| public void setup() throws AuditException, EventHubClientException { | ||
| MockitoAnnotations.initMocks(this); | ||
| } | ||
|
|
||
| @Test | ||
| public void testPredixEventProcess() throws AuditException, EventHubClientException { | ||
| AuditEvent mockAuditEvent = Mockito.mock(AuditEvent.class); | ||
| AuditEventV2 mockPredixEvent = Mockito.mock(AuditEventV2.class); | ||
| Mockito.when(mockedMapper.map(anyObject())).thenReturn(mockPredixEvent); | ||
|
|
||
| Assert.assertTrue(this.eventProcessor.process(mockAuditEvent)); | ||
| Mockito.verify(mockedClient).audit(mockPredixEvent); | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This coincidentally works since
PredixEventProcessorandPredixEventMapperare part of the same package, but it might break if trying to wire an instance ofPredixEventMapperelsewhere. Perhaps add an@Componentannotation to thePredixEventMapperclass above?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has not been addressed, frank can you please comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anurag-gujral, @FrankGasparovic has addressed this above.