This is an automated email from the ASF dual-hosted git repository.

sergehuber 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 b6e454521 Apply the restricted-event-type gate to events submitted 
over GraphQL (#870)
b6e454521 is described below

commit b6e4545219bcc35f7e00c1f8cb01b5b1d77b0178
Author: Serge Huber <[email protected]>
AuthorDate: Thu Sep 24 14:22:12 2026 +0200

    Apply the restricted-event-type gate to events submitted over GraphQL (#870)
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../graphql/commands/ProcessEventsCommand.java     | 28 +++++++-
 .../graphql/commands/ProcessEventsCommandTest.java | 82 ++++++++++++++++++++++
 2 files changed, 109 insertions(+), 1 deletion(-)

diff --git 
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/ProcessEventsCommand.java
 
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/ProcessEventsCommand.java
index e1a72ebb5..5d4147f58 100644
--- 
a/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/ProcessEventsCommand.java
+++ 
b/graphql/cxs-impl/src/main/java/org/apache/unomi/graphql/commands/ProcessEventsCommand.java
@@ -20,7 +20,9 @@ import graphql.language.InputObjectTypeDefinition;
 import graphql.schema.GraphQLInputObjectField;
 import graphql.schema.GraphQLInputObjectType;
 import org.apache.unomi.api.Event;
+import org.apache.unomi.api.ExecutionContext;
 import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.api.services.ExecutionContextManager;
 import org.apache.unomi.api.services.ProfileService;
 import org.apache.unomi.graphql.CDPGraphQLConstants;
 import org.apache.unomi.graphql.types.input.CDPConsentUpdateEventInput;
@@ -161,7 +163,15 @@ public class ProcessEventsCommand extends 
BaseCommand<Integer> {
     }
 
     private void processEvent(final Event event) {
-        int eventCode = 
serviceManager.getService(EventService.class).send(event);
+        final EventService eventService = 
serviceManager.getService(EventService.class);
+
+        if (!isEventAllowedForCurrentTenant(event, eventService,
+                serviceManager.getService(ExecutionContextManager.class))) {
+            LOGGER.debug("Event type {} is not authorized for this tenant, 
skipping it", event.getEventType());
+            return;
+        }
+
+        int eventCode = eventService.send(event);
 
         if (eventCode == EventService.PROFILE_UPDATED) {
             
serviceManager.getService(ProfileService.class).save(event.getProfile());
@@ -169,6 +179,22 @@ public class ProcessEventsCommand extends 
BaseCommand<Integer> {
         processedEventsQty.incrementAndGet();
     }
 
+    /**
+     * Same restricted-event-type check as REST {@code EventService#send} 
callers. GraphQL does not
+     * currently thread a client IP through this command, so the source IP is 
{@code null}: a
+     * restricted type is refused when the tenant has an authorized-IP list, 
and unrestricted types
+     * still pass.
+     */
+    static boolean isEventAllowedForCurrentTenant(final Event event,
+                                                  final EventService 
eventService,
+                                                  final 
ExecutionContextManager executionContextManager) {
+        final ExecutionContext executionContext = executionContextManager != 
null
+                ? executionContextManager.getCurrentContext() : null;
+        final String tenantId = executionContext != null ? 
executionContext.getTenantId() : null;
+
+        return eventService.isEventAllowedForTenant(event, tenantId, null);
+    }
+
     public static Builder create(final List<CDPEventInput> eventInputs) {
         return new Builder(eventInputs);
     }
diff --git 
a/graphql/cxs-impl/src/test/java/org/apache/unomi/graphql/commands/ProcessEventsCommandTest.java
 
b/graphql/cxs-impl/src/test/java/org/apache/unomi/graphql/commands/ProcessEventsCommandTest.java
new file mode 100644
index 000000000..a387637c9
--- /dev/null
+++ 
b/graphql/cxs-impl/src/test/java/org/apache/unomi/graphql/commands/ProcessEventsCommandTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.graphql.commands;
+
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.ExecutionContext;
+import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.services.EventService;
+import org.apache.unomi.api.services.ExecutionContextManager;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class ProcessEventsCommandTest {
+
+    private static final String TENANT_ID = "tenant-1";
+
+    @Mock
+    private EventService eventService;
+    @Mock
+    private ExecutionContextManager executionContextManager;
+    @Mock
+    private ExecutionContext executionContext;
+
+    @Test
+    void allowsEventWhenTenantGatePasses() {
+        Event event = viewEvent();
+        
when(executionContextManager.getCurrentContext()).thenReturn(executionContext);
+        when(executionContext.getTenantId()).thenReturn(TENANT_ID);
+        when(eventService.isEventAllowedForTenant(event, TENANT_ID, 
null)).thenReturn(true);
+
+        assertTrue(ProcessEventsCommand.isEventAllowedForCurrentTenant(event, 
eventService, executionContextManager));
+        verify(eventService).isEventAllowedForTenant(event, TENANT_ID, null);
+    }
+
+    @Test
+    void refusesEventWhenTenantGateFails() {
+        Event event = viewEvent();
+        
when(executionContextManager.getCurrentContext()).thenReturn(executionContext);
+        when(executionContext.getTenantId()).thenReturn(TENANT_ID);
+        when(eventService.isEventAllowedForTenant(event, TENANT_ID, 
null)).thenReturn(false);
+
+        assertFalse(ProcessEventsCommand.isEventAllowedForCurrentTenant(event, 
eventService, executionContextManager));
+        verify(eventService).isEventAllowedForTenant(event, TENANT_ID, null);
+    }
+
+    @Test
+    void passesNullTenantWhenThereIsNoExecutionContext() {
+        Event event = viewEvent();
+        when(eventService.isEventAllowedForTenant(event, null, 
null)).thenReturn(false);
+
+        assertFalse(ProcessEventsCommand.isEventAllowedForCurrentTenant(event, 
eventService, null));
+        verify(eventService).isEventAllowedForTenant(event, null, null);
+    }
+
+    private static Event viewEvent() {
+        return new Event("view", null, new Profile(), null, null, null, new 
Date());
+    }
+}

Reply via email to