This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/master by this push:
new c344c4f UNOMI-353 Integrate event type registry with ContextServlet
and EventCollector servlets (#194)
c344c4f is described below
commit c344c4f9292a4b5c033f2930fd45de60c76225ef
Author: Pavel Milkevich <[email protected]>
AuthorDate: Tue Sep 22 11:51:00 2020 +0300
UNOMI-353 Integrate event type registry with ContextServlet and
EventCollector servlets (#194)
---
.../apache/unomi/api/services/EventService.java | 15 +
.../unomi/api/services/EventTypeRegistry.java | 12 +
.../org/apache/unomi/itests/ContextServletIT.java | 803 ++++++++++++---------
.../services/impl/events/EventServiceImpl.java | 8 +
.../impl/events/EventTypeRegistryImpl.java | 77 ++
.../java/org/apache/unomi/web/ServletCommon.java | 13 +-
6 files changed, 596 insertions(+), 332 deletions(-)
diff --git a/api/src/main/java/org/apache/unomi/api/services/EventService.java
b/api/src/main/java/org/apache/unomi/api/services/EventService.java
index 2b827e6..c6136a3 100644
--- a/api/src/main/java/org/apache/unomi/api/services/EventService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/EventService.java
@@ -70,6 +70,15 @@ public interface EventService {
*/
boolean isEventAllowed(Event event, String thirdPartyId);
+
+ /**
+ * Check if event fields complies with corresponding {@link EventType}
definition
+ *
+ * @param event event to test
+ * @return true if the event is valid
+ */
+ boolean isEventValid(Event event);
+
/**
* Get the third party server name, if the request is originated from a
known peer
*
@@ -95,6 +104,12 @@ public interface EventService {
EventType getEventType(String typeName);
/**
+ * Registers event type
+ * @param eventType event type to register
+ */
+ void registerEventType(EventType eventType);
+
+ /**
* Retrieves the set of known event type identifiers.
*
* @return the set of known event type identifiers.
diff --git
a/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java
b/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java
index 121405b..c81668c 100644
--- a/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java
+++ b/api/src/main/java/org/apache/unomi/api/services/EventTypeRegistry.java
@@ -17,6 +17,7 @@
package org.apache.unomi.api.services;
+import org.apache.unomi.api.Event;
import org.apache.unomi.api.EventType;
import java.util.Collection;
@@ -28,6 +29,7 @@ public interface EventTypeRegistry {
/**
* Retrieve event type definition
+ *
* @param typeName name of the event type
* @return {@link EventType} definition
*/
@@ -35,12 +37,22 @@ public interface EventTypeRegistry {
/**
* Adds event type definition to registry
+ *
* @param eventType {@link EventType} definition
*/
void register(EventType eventType);
/**
+ * Checks if event complies with {@link EventType} definition
+ *
+ * @param event the event to validate
+ * @return result of validation
+ */
+ boolean isValid(Event event);
+
+ /**
* List all known event types
+ *
* @return Lists all known {@link EventType}s
*/
Collection<EventType> getAll();
diff --git a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
index a6dff7b..45cc2e0 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -21,7 +21,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
-import org.apache.unomi.api.*;
+import org.apache.unomi.api.ContextRequest;
+import org.apache.unomi.api.ContextResponse;
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.EventType;
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.PropertyType;
+import org.apache.unomi.api.Session;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.segments.Segment;
import org.apache.unomi.api.services.DefinitionsService;
@@ -48,10 +55,15 @@ import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import static org.hamcrest.core.IsCollectionContaining.hasItem;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
/**
@@ -61,330 +73,465 @@ import static org.junit.Assert.*;
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerSuite.class)
public class ContextServletIT extends BaseIT {
- private final static String CONTEXT_URL = "/context.json";
- private final static String THIRD_PARTY_HEADER_NAME = "X-Unomi-Peer";
- private final static String SEGMENT_EVENT_TYPE = "test-event-type";
- private final static String SEGMENT_ID = "test-segment-id";
- private final static int SEGMENT_NUMBER_OF_DAYS = 30;
-
- private ObjectMapper objectMapper = new ObjectMapper();
-
- @Inject
- @Filter(timeout = 600000)
- protected EventService eventService;
-
- @Inject
- @Filter(timeout = 600000)
- protected PersistenceService persistenceService;
-
- @Inject
- @Filter(timeout = 600000)
- protected ProfileService profileService;
-
- @Inject
- @Filter(timeout = 600000)
- protected DefinitionsService definitionsService;
-
- @Inject
- @Filter(timeout = 600000)
- protected SegmentService segmentService;
-
- private Profile profile;
-
- @Before
- public void setUp() throws InterruptedException {
- //Create a past-event segment
- Metadata segmentMetadata = new Metadata(SEGMENT_ID);
- Segment segment = new Segment(segmentMetadata);
- Condition segmentCondition = new
Condition(definitionsService.getConditionType("pastEventCondition"));
- segmentCondition.setParameter("minimumEventCount",2);
-
segmentCondition.setParameter("numberOfDays",SEGMENT_NUMBER_OF_DAYS);
- Condition pastEventEventCondition = new
Condition(definitionsService.getConditionType("eventTypeCondition"));
-
pastEventEventCondition.setParameter("eventTypeId",SEGMENT_EVENT_TYPE);
-
segmentCondition.setParameter("eventCondition",pastEventEventCondition);
- segment.setCondition(segmentCondition);
- segmentService.setSegmentDefinition(segment);
-
- String profileId = "test-profile-id";
- profile = new Profile(profileId);
- profileService.save(profile);
-
- refreshPersistence();
- }
-
- @After
- public void tearDown() {
- TestUtils.removeAllEvents(definitionsService,
persistenceService);
- TestUtils.removeAllSessions(definitionsService,
persistenceService);
- TestUtils.removeAllProfiles(definitionsService,
persistenceService);
- profileService.delete(profile.getItemId(), false);
- segmentService.removeSegmentDefinition(SEGMENT_ID,false);
- }
-
- @Test
- public void testUpdateEventFromContextAuthorizedThirdParty_Success()
throws IOException, InterruptedException {
- //Arrange
- String eventId = "test-event-id-" + System.currentTimeMillis();
- String profileId = "test-profile-id";
- String sessionId = "test-session-id";
- String scope = "test-scope";
- String eventTypeOriginal = "test-event-type-original";
- String eventTypeUpdated = "test-event-type-updated";
- Profile profile = new Profile(profileId);
- Session session = new Session(sessionId, profile, new Date(),
scope);
- Event event = new Event(eventId, eventTypeOriginal, session,
profile, scope, null, null, new Date());
- profileService.save(profile);
- this.eventService.send(event);
- refreshPersistence();
- Thread.sleep(2000);
- event.setEventType(eventTypeUpdated); //change the event so we
can see the update effect
-
- //Act
- ContextRequest contextRequest = new ContextRequest();
- contextRequest.setSessionId(session.getItemId());
- contextRequest.setEvents(Arrays.asList(event));
- HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
- request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
- TestUtils.executeContextJSONRequest(request, sessionId);
- refreshPersistence();
- Thread.sleep(2000); //Making sure event is updated in DB
-
- //Assert
- event = this.eventService.getEvent(eventId);
- assertEquals(2, event.getVersion().longValue());
- assertEquals(eventTypeUpdated,event.getEventType());
- }
-
- @Test
- public void testUpdateEventFromContextUnAuthorizedThirdParty_Fail()
throws IOException, InterruptedException {
- //Arrange
- String eventId = "test-event-id-" + System.currentTimeMillis();
- String profileId = "test-profile-id";
- String sessionId = "test-session-id";
- String scope = "test-scope";
- String eventTypeOriginal = "test-event-type-original";
- String eventTypeUpdated = "test-event-type-updated";
- Profile profile = new Profile(profileId);
- Session session = new Session(sessionId, profile, new Date(),
scope);
- Event event = new Event(eventId, eventTypeOriginal, session,
profile, scope, null, null, new Date());
- profileService.save(profile);
- this.eventService.send(event);
- refreshPersistence();
- Thread.sleep(2000);
- event.setEventType(eventTypeUpdated); //change the event so we
can see the update effect
-
- //Act
- ContextRequest contextRequest = new ContextRequest();
- contextRequest.setSessionId(session.getItemId());
- contextRequest.setEvents(Arrays.asList(event));
- HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
- TestUtils.executeContextJSONRequest(request, sessionId);
- refreshPersistence();
- Thread.sleep(2000); //Making sure event is updated in DB
-
- //Assert
- event = this.eventService.getEvent(eventId);
- assertEquals(1, event.getVersion().longValue());
- assertEquals(eventTypeOriginal,event.getEventType());
- }
-
-
- @Test
- public void
testUpdateEventFromContextAuthorizedThirdPartyNoItemID_Fail() throws
IOException, InterruptedException {
- //Arrange
- String eventId = "test-event-id-" + System.currentTimeMillis();
- String sessionId = "test-session-id";
- String scope = "test-scope";
- String eventTypeOriginal = "test-event-type-original";
- String eventTypeUpdated = "test-event-type-updated";
- Session session = new Session(sessionId, profile, new Date(),
scope);
- Event event = new Event(eventId, eventTypeOriginal, session,
profile, scope, null, null, new Date());
- this.eventService.send(event);
- refreshPersistence();
- Thread.sleep(2000);
- event.setEventType(eventTypeUpdated); //change the event so we
can see the update effect
-
- //Act
- ContextRequest contextRequest = new ContextRequest();
- contextRequest.setSessionId(session.getItemId());
- contextRequest.setEvents(Arrays.asList(event));
- HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
- TestUtils.executeContextJSONRequest(request, sessionId);
- refreshPersistence();
- Thread.sleep(2000); //Making sure event is updated in DB
-
- //Assert
- event = this.eventService.getEvent(eventId);
- assertEquals(1, event.getVersion().longValue());
- assertEquals(eventTypeOriginal,event.getEventType());
- }
-
- @Test
- public void
testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws
IOException, InterruptedException {
- //Arrange
- String sessionId = "test-session-id";
- String scope = "test-scope";
- Event event = new Event();
- event.setEventType(SEGMENT_EVENT_TYPE);
- event.setScope(scope);
-
- //Act
- ContextRequest contextRequest = new ContextRequest();
- contextRequest.setSessionId(sessionId);
- contextRequest.setRequireSegments(true);
- contextRequest.setEvents(Arrays.asList(event));
- HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
- String cookieHeaderValue =
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
- refreshPersistence();
- Thread.sleep(1000); //Making sure DB is updated
-
- //Add the context-profile-id cookie to the second event
- request.addHeader("Cookie", cookieHeaderValue);
- ContextResponse response =
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse();
//second event
-
- refreshPersistence();
-
- //Assert
- assertEquals(1, response.getProfileSegments().size());
- assertThat(response.getProfileSegments(),hasItem(SEGMENT_ID));
- }
-
- @Test
- public void
testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegment() throws
IOException, InterruptedException {
- //Arrange
- String sessionId = "test-session-id";
- String scope = "test-scope";
- Event event = new Event();
- event.setEventType(SEGMENT_EVENT_TYPE);
- event.setScope(scope);
- String regularURI = URL + CONTEXT_URL;
- long oldTimestamp =
LocalDateTime.now(ZoneId.of("UTC")).minusDays(SEGMENT_NUMBER_OF_DAYS +
1).toInstant(ZoneOffset.UTC).toEpochMilli();
- String customTimestampURI = regularURI + "?timestamp=" +
oldTimestamp;
-
- //Act
- ContextRequest contextRequest = new ContextRequest();
- contextRequest.setSessionId(sessionId);
- contextRequest.setRequireSegments(true);
- contextRequest.setEvents(Arrays.asList(event));
- HttpPost request = new HttpPost(regularURI);
- request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
- //The first event is with a default timestamp (now)
- String cookieHeaderValue =
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
- refreshPersistence();
- //The second event is with a customized timestamp
- request.setURI(URI.create(customTimestampURI));
- request.addHeader("Cookie", cookieHeaderValue);
- ContextResponse response =
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse();
//second event
- refreshPersistence();
-
- //Assert
- assertEquals(0,response.getProfileSegments().size());
- }
-
- @Test
- public void
testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSegment()
throws IOException, InterruptedException {
- //Arrange
- String sessionId = "test-session-id";
- String scope = "test-scope";
- Event event = new Event();
- event.setEventType(SEGMENT_EVENT_TYPE);
- event.setScope(scope);
- String regularURI = URL + CONTEXT_URL;
- long futureTimestamp =
LocalDateTime.now(ZoneId.of("UTC")).plusDays(1).toInstant(ZoneOffset.UTC).toEpochMilli();
- String customTimestampURI = regularURI + "?timestamp=" +
futureTimestamp;
-
- //Act
- ContextRequest contextRequest = new ContextRequest();
- contextRequest.setSessionId(sessionId);
- contextRequest.setRequireSegments(true);
- contextRequest.setEvents(Arrays.asList(event));
- HttpPost request = new HttpPost(regularURI);
- request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
- //The first event is with a default timestamp (now)
- String cookieHeaderValue =
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
- refreshPersistence();
- //The second event is with a customized timestamp
- request.setURI(URI.create(customTimestampURI));
- request.addHeader("Cookie", cookieHeaderValue);
- ContextResponse response =
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse();
//second event
- refreshPersistence();
-
- //Assert
- assertEquals(0,response.getProfileSegments().size());
- }
-
- @Test
- public void testCreateEventWithProfileId_Success() throws IOException,
InterruptedException {
- //Arrange
- String eventId = "test-event-id-" + System.currentTimeMillis();
- String profileId = "test-profile-id";
- String eventType = "test-event-type";
- Event event = new Event();
- event.setEventType(eventType);
- event.setItemId(eventId);
-
- ContextRequest contextRequest = new ContextRequest();
- contextRequest.setProfileId(profileId);
- contextRequest.setEvents(Arrays.asList(event));
-
- //Act
- HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
- request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
- TestUtils.executeContextJSONRequest(request);
- refreshPersistence();
- Thread.sleep(2000); //Making sure event is updated in DB
-
- //Assert
- Profile profile = this.profileService.load(profileId);
- assertEquals(profileId, profile.getItemId());
- }
-
- @Test
- public void testOGNLVulnerability() throws IOException,
InterruptedException {
-
- File vulnFile = new File("target/vuln-file.txt");
- if (vulnFile.exists()) {
- vulnFile.delete();
- }
- String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
- vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\",
"\\\\"); // this is required for Windows support
-
- Map<String,String> parameters = new HashMap<>();
- parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
- HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new
StringEntity(getValidatedBundleJSON("security/ognl-payload-1.json",
parameters), ContentType.create("application/json")));
- TestUtils.executeContextJSONRequest(request);
- refreshPersistence();
- Thread.sleep(2000); //Making sure event is updated in DB
-
- assertFalse("Vulnerability successfully executed ! File created
at " + vulnFileCanonicalPath, vulnFile.exists());
-
- }
-
- @Test
- public void testMVELVulnerability() throws IOException,
InterruptedException {
-
- File vulnFile = new File("target/vuln-file.txt");
- if (vulnFile.exists()) {
- vulnFile.delete();
- }
- String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
- vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\",
"\\\\"); // this is required for Windows support
-
- Map<String,String> parameters = new HashMap<>();
- parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
- HttpPost request = new HttpPost(URL + CONTEXT_URL);
- request.setEntity(new
StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json",
parameters), ContentType.create("application/json")));
- TestUtils.executeContextJSONRequest(request);
- refreshPersistence();
- Thread.sleep(2000); //Making sure event is updated in DB
-
- assertFalse("Vulnerability successfully executed ! File created
at " + vulnFileCanonicalPath, vulnFile.exists());
-
- }
+ private final static String CONTEXT_URL = "/context.json";
+ private final static String THIRD_PARTY_HEADER_NAME = "X-Unomi-Peer";
+ private final static String SEGMENT_EVENT_TYPE = "test-event-type";
+ private final static String SEGMENT_ID = "test-segment-id";
+ private final static int SEGMENT_NUMBER_OF_DAYS = 30;
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected EventService eventService;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected PersistenceService persistenceService;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected ProfileService profileService;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected DefinitionsService definitionsService;
+
+ @Inject
+ @Filter(timeout = 600000)
+ protected SegmentService segmentService;
+
+ private Profile profile;
+
+ @Before
+ public void setUp() throws InterruptedException {
+ this.registerEventType(SEGMENT_EVENT_TYPE);
+
+ //Create a past-event segment
+ Metadata segmentMetadata = new Metadata(SEGMENT_ID);
+ Segment segment = new Segment(segmentMetadata);
+ Condition segmentCondition = new
Condition(definitionsService.getConditionType("pastEventCondition"));
+ segmentCondition.setParameter("minimumEventCount", 2);
+ segmentCondition.setParameter("numberOfDays", SEGMENT_NUMBER_OF_DAYS);
+ Condition pastEventEventCondition = new
Condition(definitionsService.getConditionType("eventTypeCondition"));
+ pastEventEventCondition.setParameter("eventTypeId",
SEGMENT_EVENT_TYPE);
+ segmentCondition.setParameter("eventCondition",
pastEventEventCondition);
+ segment.setCondition(segmentCondition);
+ segmentService.setSegmentDefinition(segment);
+
+ String profileId = "test-profile-id";
+ profile = new Profile(profileId);
+ profileService.save(profile);
+
+ refreshPersistence();
+ }
+
+ @After
+ public void tearDown() {
+ TestUtils.removeAllEvents(definitionsService, persistenceService);
+ TestUtils.removeAllSessions(definitionsService, persistenceService);
+ TestUtils.removeAllProfiles(definitionsService, persistenceService);
+ profileService.delete(profile.getItemId(), false);
+ segmentService.removeSegmentDefinition(SEGMENT_ID, false);
+ }
+
+ private void registerEventType(final String type) {
+ final Set<PropertyType> props = new HashSet<>();
+ registerEventType(type, props);
+ }
+
+ private void registerEventType(final String type, final Set<PropertyType>
props) {
+ final EventType eventType = new EventType(type, props, 1);
+ eventService.registerEventType(eventType);
+ }
+
+ @Test
+ public void testUpdateEventFromContextAuthorizedThirdParty_Success()
throws IOException, InterruptedException {
+ //Arrange
+ String eventId = "test-event-id-" + System.currentTimeMillis();
+ String profileId = "test-profile-id";
+ String sessionId = "test-session-id";
+ String scope = "test-scope";
+ String eventTypeOriginal = "test-event-type-original";
+ String eventTypeUpdated = "test-event-type-updated";
+ Profile profile = new Profile(profileId);
+ Session session = new Session(sessionId, profile, new Date(), scope);
+ Event event = new Event(eventId, eventTypeOriginal, session, profile,
scope, null, null, new Date());
+ profileService.save(profile);
+ this.eventService.send(event);
+ refreshPersistence();
+ Thread.sleep(2000);
+ event.setEventType(eventTypeUpdated); //change the event so we can see
the update effect
+
+ this.registerEventType(eventTypeUpdated);
+
+ //Act
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setSessionId(session.getItemId());
+ contextRequest.setEvents(Arrays.asList(event));
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request, sessionId);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ //Assert
+ event = this.eventService.getEvent(eventId);
+ assertEquals(2, event.getVersion().longValue());
+ assertEquals(eventTypeUpdated, event.getEventType());
+ }
+
+ @Test
+ public void testUpdateEventFromContextUnAuthorizedThirdParty_Fail() throws
IOException, InterruptedException {
+ //Arrange
+ String eventId = "test-event-id-" + System.currentTimeMillis();
+ String profileId = "test-profile-id";
+ String sessionId = "test-session-id";
+ String scope = "test-scope";
+ String eventTypeOriginal = "test-event-type-original";
+ String eventTypeUpdated = "test-event-type-updated";
+ Profile profile = new Profile(profileId);
+ Session session = new Session(sessionId, profile, new Date(), scope);
+ Event event = new Event(eventId, eventTypeOriginal, session, profile,
scope, null, null, new Date());
+ profileService.save(profile);
+ this.eventService.send(event);
+ refreshPersistence();
+ Thread.sleep(2000);
+ event.setEventType(eventTypeUpdated); //change the event so we can see
the update effect
+
+ this.registerEventType(eventTypeUpdated);
+
+ //Act
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setSessionId(session.getItemId());
+ contextRequest.setEvents(Arrays.asList(event));
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request, sessionId);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ //Assert
+ event = this.eventService.getEvent(eventId);
+ assertEquals(1, event.getVersion().longValue());
+ assertEquals(eventTypeOriginal, event.getEventType());
+ }
+
+
+ @Test
+ public void testUpdateEventFromContextAuthorizedThirdPartyNoItemID_Fail()
throws IOException, InterruptedException {
+ //Arrange
+ String eventId = "test-event-id-" + System.currentTimeMillis();
+ String sessionId = "test-session-id";
+ String scope = "test-scope";
+ String eventTypeOriginal = "test-event-type-original";
+ String eventTypeUpdated = "test-event-type-updated";
+ Session session = new Session(sessionId, profile, new Date(), scope);
+ Event event = new Event(eventId, eventTypeOriginal, session, profile,
scope, null, null, new Date());
+ this.eventService.send(event);
+ refreshPersistence();
+ Thread.sleep(2000);
+ event.setEventType(eventTypeUpdated); //change the event so we can see
the update effect
+
+ this.registerEventType(eventTypeUpdated);
+
+ //Act
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setSessionId(session.getItemId());
+ contextRequest.setEvents(Arrays.asList(event));
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request, sessionId);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ //Assert
+ event = this.eventService.getEvent(eventId);
+ assertEquals(1, event.getVersion().longValue());
+ assertEquals(eventTypeOriginal, event.getEventType());
+ }
+
+ @Test
+ public void testCreateEventsWithNoTimestampParam_profileAddedToSegment()
throws IOException, InterruptedException {
+ //Arrange
+ String sessionId = "test-session-id";
+ String scope = "test-scope";
+ Event event = new Event();
+ event.setEventType(SEGMENT_EVENT_TYPE);
+ event.setScope(scope);
+
+ //Act
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setSessionId(sessionId);
+ contextRequest.setRequireSegments(true);
+ contextRequest.setEvents(Arrays.asList(event));
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ String cookieHeaderValue =
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+ refreshPersistence();
+ Thread.sleep(1000); //Making sure DB is updated
+
+ //Add the context-profile-id cookie to the second event
+ request.addHeader("Cookie", cookieHeaderValue);
+ ContextResponse response =
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse();
//second event
+
+ refreshPersistence();
+
+ //Assert
+ assertEquals(1, response.getProfileSegments().size());
+ assertThat(response.getProfileSegments(), hasItem(SEGMENT_ID));
+ }
+
+ @Test
+ public void
testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegment() throws
IOException, InterruptedException {
+ //Arrange
+ String sessionId = "test-session-id";
+ String scope = "test-scope";
+ Event event = new Event();
+ event.setEventType(SEGMENT_EVENT_TYPE);
+ event.setScope(scope);
+ String regularURI = URL + CONTEXT_URL;
+ long oldTimestamp =
LocalDateTime.now(ZoneId.of("UTC")).minusDays(SEGMENT_NUMBER_OF_DAYS +
1).toInstant(ZoneOffset.UTC).toEpochMilli();
+ String customTimestampURI = regularURI + "?timestamp=" + oldTimestamp;
+
+ //Act
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setSessionId(sessionId);
+ contextRequest.setRequireSegments(true);
+ contextRequest.setEvents(Arrays.asList(event));
+ HttpPost request = new HttpPost(regularURI);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ //The first event is with a default timestamp (now)
+ String cookieHeaderValue =
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+ refreshPersistence();
+ //The second event is with a customized timestamp
+ request.setURI(URI.create(customTimestampURI));
+ request.addHeader("Cookie", cookieHeaderValue);
+ ContextResponse response =
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse();
//second event
+ refreshPersistence();
+
+ //Assert
+ assertEquals(0, response.getProfileSegments().size());
+ }
+
+ @Test
+ public void
testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSegment()
throws IOException, InterruptedException {
+ //Arrange
+ String sessionId = "test-session-id";
+ String scope = "test-scope";
+ Event event = new Event();
+ event.setEventType(SEGMENT_EVENT_TYPE);
+ event.setScope(scope);
+ String regularURI = URL + CONTEXT_URL;
+ long futureTimestamp =
LocalDateTime.now(ZoneId.of("UTC")).plusDays(1).toInstant(ZoneOffset.UTC).toEpochMilli();
+ String customTimestampURI = regularURI + "?timestamp=" +
futureTimestamp;
+
+ //Act
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setSessionId(sessionId);
+ contextRequest.setRequireSegments(true);
+ contextRequest.setEvents(Arrays.asList(event));
+ HttpPost request = new HttpPost(regularURI);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ //The first event is with a default timestamp (now)
+ String cookieHeaderValue =
TestUtils.executeContextJSONRequest(request, sessionId).getCookieHeaderValue();
+ refreshPersistence();
+ //The second event is with a customized timestamp
+ request.setURI(URI.create(customTimestampURI));
+ request.addHeader("Cookie", cookieHeaderValue);
+ ContextResponse response =
(TestUtils.executeContextJSONRequest(request, sessionId)).getContextResponse();
//second event
+ refreshPersistence();
+
+ //Assert
+ assertEquals(0, response.getProfileSegments().size());
+ }
+
+ @Test
+ public void testCreateEventWithProfileId_Success() throws IOException,
InterruptedException {
+ //Arrange
+ String eventId = "test-event-id-" + System.currentTimeMillis();
+ String profileId = "test-profile-id";
+ String eventType = "test-event-type";
+ Event event = new Event();
+ event.setEventType(eventType);
+ event.setItemId(eventId);
+
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setProfileId(profileId);
+ contextRequest.setEvents(Arrays.asList(event));
+
+ this.registerEventType(eventType);
+
+ //Act
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ //Assert
+ Profile profile = this.profileService.load(profileId);
+ assertEquals(profileId, profile.getItemId());
+ }
+
+ @Test
+ public void testCreateEventWithPropertiesValidation_Success() throws
IOException, InterruptedException {
+ //Arrange
+ String eventId = "valid-event-id-" + System.currentTimeMillis();
+ String profileId = "valid-profile-id";
+ String eventType = "valid-event-type";
+ Event event = new Event();
+ event.setEventType(eventType);
+ event.setItemId(eventId);
+ Map<String, Object> props = new HashMap<>();
+ props.put("floatProperty", 3.14159);
+ event.setProperties(props);
+
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setProfileId(profileId);
+ contextRequest.setEvents(Arrays.asList(event));
+
+ final Set<PropertyType> typeProps = new HashSet<>();
+ PropertyType floatProp = new PropertyType();
+ floatProp.setItemId("floatProperty");
+ floatProp.setValueTypeId("float");
+ typeProps.add(floatProp);
+ this.registerEventType(eventType, typeProps);
+
+ //Act
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ //Assert
+ event = this.eventService.getEvent(eventId);
+ assertEquals(eventType, event.getEventType());
+ assertEquals(3.14159, event.getProperty("floatProperty"));
+ }
+
+ @Test
+ public void testCreateEventWithPropertyValueValidation_Failure() throws
IOException, InterruptedException {
+ //Arrange
+ String eventId = "invalid-event-value-id-" +
System.currentTimeMillis();
+ String profileId = "invalid-profile-id";
+ String eventType = "invalid-event-value-type";
+ Event event = new Event();
+ event.setEventType(eventType);
+ event.setItemId(eventId);
+ Map<String, Object> props = new HashMap<>();
+ props.put("floatProperty", "Invalid value");
+ event.setProperties(props);
+
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setProfileId(profileId);
+ contextRequest.setEvents(Arrays.asList(event));
+
+ final Set<PropertyType> typeProps = new HashSet<>();
+ PropertyType floatProp = new PropertyType();
+ floatProp.setItemId("floatProperty");
+ floatProp.setValueTypeId("float");
+ typeProps.add(floatProp);
+ this.registerEventType(eventType, typeProps);
+
+ //Act
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ //Assert
+ event = this.eventService.getEvent(eventId);
+ assertNull(event);
+ }
+
+ @Test
+ public void testCreateEventWithPropertyNameValidation_Failure() throws
IOException, InterruptedException {
+ //Arrange
+ String eventId = "invalid-event-prop-id-" + System.currentTimeMillis();
+ String profileId = "invalid-profile-id";
+ String eventType = "invalid-event-prop-type";
+ Event event = new Event();
+ event.setEventType(eventType);
+ event.setItemId(eventId);
+ Map<String, Object> props = new HashMap<>();
+ props.put("floatProperty", 3.14159);
+ event.setProperties(props);
+
+ ContextRequest contextRequest = new ContextRequest();
+ contextRequest.setProfileId(profileId);
+ contextRequest.setEvents(Arrays.asList(event));
+
+ final Set<PropertyType> typeProps = new HashSet<>();
+ PropertyType floatProp = new PropertyType();
+ floatProp.setItemId("floatProperty");
+ floatProp.setValueTypeId("float");
+ PropertyType geopointProp = new PropertyType();
+ geopointProp.setItemId("geopointProperty");
+ geopointProp.setValueTypeId("geopoint");
+ typeProps.add(geopointProp);
+ this.registerEventType(eventType, typeProps);
+
+ //Act
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.addHeader(THIRD_PARTY_HEADER_NAME, UNOMI_KEY);
+ request.setEntity(new
StringEntity(objectMapper.writeValueAsString(contextRequest),
ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ //Assert
+ event = this.eventService.getEvent(eventId);
+ assertNull(event);
+ }
+
+ @Test
+ public void testOGNLVulnerability() throws IOException,
InterruptedException {
+
+ File vulnFile = new File("target/vuln-file.txt");
+ if (vulnFile.exists()) {
+ vulnFile.delete();
+ }
+ String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
+ vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\", "\\\\");
// this is required for Windows support
+
+ Map<String, String> parameters = new HashMap<>();
+ parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.setEntity(new
StringEntity(getValidatedBundleJSON("security/ognl-payload-1.json",
parameters), ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ assertFalse("Vulnerability successfully executed ! File created at " +
vulnFileCanonicalPath, vulnFile.exists());
+
+ }
+
+ @Test
+ public void testMVELVulnerability() throws IOException,
InterruptedException {
+
+ File vulnFile = new File("target/vuln-file.txt");
+ if (vulnFile.exists()) {
+ vulnFile.delete();
+ }
+ String vulnFileCanonicalPath = vulnFile.getCanonicalPath();
+ vulnFileCanonicalPath = vulnFileCanonicalPath.replace("\\", "\\\\");
// this is required for Windows support
+
+ Map<String, String> parameters = new HashMap<>();
+ parameters.put("VULN_FILE_PATH", vulnFileCanonicalPath);
+ HttpPost request = new HttpPost(URL + CONTEXT_URL);
+ request.setEntity(new
StringEntity(getValidatedBundleJSON("security/mvel-payload-1.json",
parameters), ContentType.create("application/json")));
+ TestUtils.executeContextJSONRequest(request);
+ refreshPersistence();
+ Thread.sleep(2000); //Making sure event is updated in DB
+
+ assertFalse("Vulnerability successfully executed ! File created at " +
vulnFileCanonicalPath, vulnFile.exists());
+
+ }
}
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
index 6d51f16..3f5cd23 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/events/EventServiceImpl.java
@@ -131,6 +131,10 @@ public class EventServiceImpl implements EventService {
return true;
}
+ public boolean isEventValid(Event event) {
+ return this.eventTypeRegistry.isValid(event);
+ }
+
public String authenticateThirdPartyServer(String key, String ip) {
logger.debug("Authenticating third party server with key: " + key + "
and IP: " + ip);
if (key != null) {
@@ -207,6 +211,10 @@ public class EventServiceImpl implements EventService {
return eventTypeRegistry.get(typeName);
}
+ public void registerEventType(final EventType eventType) {
+ eventTypeRegistry.register(eventType);
+ }
+
@Override
public List<EventProperty> getEventProperties() {
Map<String, Map<String, Object>> mappings =
persistenceService.getPropertiesMapping(Event.ITEM_TYPE);
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
index 9525e19..02b1079 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/events/EventTypeRegistryImpl.java
@@ -17,8 +17,11 @@
package org.apache.unomi.services.impl.events;
+import org.apache.unomi.api.Event;
import org.apache.unomi.api.EventType;
+import org.apache.unomi.api.GeoPoint;
import org.apache.unomi.api.PluginType;
+import org.apache.unomi.api.PropertyType;
import org.apache.unomi.api.services.EventTypeRegistry;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.osgi.framework.Bundle;
@@ -31,11 +34,13 @@ import org.slf4j.LoggerFactory;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
public class EventTypeRegistryImpl implements EventTypeRegistry,
SynchronousBundleListener {
@@ -89,6 +94,78 @@ public class EventTypeRegistryImpl implements
EventTypeRegistry, SynchronousBund
eventTypes.put(eventType.getType(), eventType);
}
+ @Override
+ public boolean isValid(Event event) {
+ if (event == null) {
+ return false;
+ }
+ final EventType eventType = this.get(event.getEventType());
+ if (eventType == null) {
+ return false;
+ }
+
+ return areAllPropertiesValid(event.getProperties(),
eventType.getPropertyTypes());
+ }
+
+ /**
+ * Checks that all properties from map are defined in the property type
set.
+ * Does not require all defined properties to be present in map.
+ *
+ * @param props map of properties to validate
+ * @param types set of a predefined event type properties
+ * @return boolean result of validation
+ */
+ private boolean areAllPropertiesValid(Map<String, Object> props,
Set<PropertyType> types) {
+ if (props == null || props.isEmpty() || types == null ||
types.isEmpty()) {
+ return true;
+ }
+ return props.entrySet().stream().allMatch(entry -> {
+ return types.stream().anyMatch(type -> {
+ if (!type.getItemId().equals(entry.getKey())) {
+ return false;
+ }
+ final Set<PropertyType> childTypes =
type.getChildPropertyTypes();
+ if (childTypes.size() > 0 && entry.getValue() != null) {
+ try {
+ final Map<String, Object> childProps = (Map<String,
Object>) entry.getValue();
+ return areAllPropertiesValid(childProps, childTypes);
+ } catch (ClassCastException e) {
+ logger.error("Event property '{}' value is invalid:
{}", entry.getKey(), e.getCause());
+ return false;
+ }
+ } else {
+ return testValueType(entry.getValue(),
type.getValueTypeId());
+ }
+ });
+ });
+ }
+
+ private boolean testValueType(final Object value, final String
valueTypeId) {
+ switch (valueTypeId) {
+ case "integer":
+ return value instanceof Integer;
+ case "long":
+ return value instanceof Long;
+ case "float":
+ return value instanceof Double;
+ case "set":
+ case "json":
+ return value instanceof Map;
+ case "geopoint":
+ return value instanceof GeoPoint;
+ case "date":
+ return value instanceof Date;
+ case "boolean":
+ return value instanceof Boolean;
+ case "id":
+ case "string":
+ return value instanceof String;
+ default:
+ // return true if type is unknown cuz it may be custom
+ return true;
+ }
+ }
+
public Collection<EventType> getAll() {
return this.eventTypes.values();
}
diff --git a/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
b/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
index 597295f..bf700c3 100644
--- a/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
+++ b/wab/src/main/java/org/apache/unomi/web/ServletCommon.java
@@ -55,8 +55,8 @@ public class ServletCommon {
}
public static Changes handleEvents(List<Event> events, Session session,
Profile profile,
- ServletRequest request, ServletResponse
response, Date timestamp,
- PrivacyService privacyService,
EventService eventService) {
+ ServletRequest request, ServletResponse
response, Date timestamp,
+ PrivacyService privacyService,
EventService eventService) {
List<String> filteredEventTypes =
privacyService.getFilteredEventTypes(profile);
String thirdPartyId =
eventService.authenticateThirdPartyServer(((HttpServletRequest)
request).getHeader("X-Unomi-Peer"),
@@ -69,6 +69,11 @@ public class ServletCommon {
for (Event event : events) {
processedEventsCnt++;
if (event.getEventType() != null) {
+ if (!eventService.isEventValid(event)) {
+ logger.warn("Event is not valid : {}",
event.getEventType());
+ continue;
+ }
+
Event eventToSend = new Event(event.getEventType(),
session, profile, event.getScope(), event.getSource(),
event.getTarget(), event.getProperties(),
timestamp, event.isPersistent());
if (!eventService.isEventAllowed(event, thirdPartyId)) {
@@ -90,7 +95,7 @@ public class ServletCommon {
eventToSend.getAttributes().put(Event.HTTP_REQUEST_ATTRIBUTE, request);
eventToSend.getAttributes().put(Event.HTTP_RESPONSE_ATTRIBUTE, response);
logger.debug("Received event " + event.getEventType() + "
for profile=" + profile.getItemId() + " session="
- + (session!= null?session.getItemId():null) + "
target=" + event.getTarget() + " timestamp=" + timestamp);
+ + (session != null ? session.getItemId() : null) +
" target=" + event.getTarget() + " timestamp=" + timestamp);
changes = eventService.send(eventToSend);
// If the event execution changes the profile we need to
update it so the next event use the right profile
if ((changes & EventService.PROFILE_UPDATED) ==
EventService.PROFILE_UPDATED) {
@@ -99,7 +104,7 @@ public class ServletCommon {
if ((changes & EventService.ERROR) == EventService.ERROR) {
//Don't count the event that failed
processedEventsCnt--;
- logger.error("Error processing events. Total number of
processed events: {}/{}", processedEventsCnt,events.size());
+ logger.error("Error processing events. Total number of
processed events: {}/{}", processedEventsCnt, events.size());
break;
}
}