You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by ch...@apache.org on 2026/08/13 03:43:42 UTC

(atlas) branch master updated: ATLAS-5372: Prevent cross-user overwrite in saved search create API via caller-supplied guid (#724)

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

chaitalicod pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/master by this push:
     new c1cc1ff91 ATLAS-5372: Prevent cross-user overwrite in saved search create API via caller-supplied guid (#724)
c1cc1ff91 is described below

commit c1cc1ff9114f19248ec642d61eb861a1dab75919
Author: chaitalicod <36...@users.noreply.github.com>
AuthorDate: Thu Aug 13 09:13:37 2026 +0530

    ATLAS-5372: Prevent cross-user overwrite in saved search create API via caller-supplied guid (#724)
    
    Co-authored-by: chaitalithombare <ch...@apache.org>
---
 .../atlas/discovery/EntityDiscoveryService.java    |  6 ++
 .../discovery/EntityDiscoveryServiceTest.java      | 79 ++++++++++++++++++++++
 2 files changed, 85 insertions(+)

diff --git a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java
index 410a42f1c..3f5990418 100644
--- a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java
+++ b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java
@@ -699,6 +699,12 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
 
             checkSavedSearchOwnership(currentUser, savedSearch);
 
+            if (StringUtils.isNotBlank(savedSearch.getGuid())) {
+                AtlasUserSavedSearch existingSavedSearch = userProfileService.getSavedSearch(savedSearch.getGuid());
+
+                checkSavedSearchOwnership(currentUser, existingSavedSearch);
+            }
+
             return userProfileService.addSavedSearch(savedSearch);
         } catch (AtlasBaseException e) {
             LOG.error("addSavedSearch({})", savedSearch, e);
diff --git a/repository/src/test/java/org/apache/atlas/discovery/EntityDiscoveryServiceTest.java b/repository/src/test/java/org/apache/atlas/discovery/EntityDiscoveryServiceTest.java
index df01f3bcf..2bc0177c0 100644
--- a/repository/src/test/java/org/apache/atlas/discovery/EntityDiscoveryServiceTest.java
+++ b/repository/src/test/java/org/apache/atlas/discovery/EntityDiscoveryServiceTest.java
@@ -18,6 +18,7 @@
 package org.apache.atlas.discovery;
 
 import org.apache.atlas.AtlasConfiguration;
+import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.RequestContext;
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.discovery.AtlasQuickSearchResult;
@@ -60,9 +61,13 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 public class EntityDiscoveryServiceTest {
     @Mock
@@ -149,6 +154,32 @@ public class EntityDiscoveryServiceTest {
         when(entityDiscoveryService.searchGUIDsWithParameters(any(AtlasAuditAgingType.class), any(Set.class), any(SearchParameters.class))).thenReturn(new HashSet<>());
     }
 
+    private EntityDiscoveryService createStrictServiceForSecurityTests() throws Exception {
+        /*
+         * Strict setup path for security-sensitive tests:
+         * always instantiate a real EntityDiscoveryService and never
+         * fallback to a mocked/spied service.
+         */
+        when(typeRegistry.getEntityTypeByName(anyString())).thenReturn(entityType);
+        when(entityType.getTypeAndAllSubTypesQryStr()).thenReturn("(TestType)");
+        when(entityType.getAttribute(anyString())).thenReturn(attribute);
+        when(entityType.getTypeName()).thenReturn("TestType");
+        when(attribute.getVertexPropertyName()).thenReturn("v.testAttr");
+        when(attribute.getAttributeType()).thenReturn(mock(org.apache.atlas.type.AtlasType.class));
+        when(indexer.getVertexIndexKeys()).thenReturn(Collections.emptySet());
+        when(indexer.getEdgeIndexKeys()).thenReturn(Collections.emptySet());
+        when(graph.indexQuery(anyString(), anyString())).thenReturn(indexQuery);
+        when(graph.query()).thenReturn(mock(org.apache.atlas.repository.graphdb.AtlasGraphQuery.class));
+        when(indexQuery.vertices()).thenReturn(Collections.emptyIterator());
+        when(indexQuery.vertexTotals()).thenReturn(0L);
+
+        RequestContext context = mock(RequestContext.class);
+        when(RequestContext.get()).thenReturn(context);
+        when(context.getUser()).thenReturn("testUser");
+
+        return new EntityDiscoveryService(typeRegistry, graph, indexer, searchTracker, userProfileService, taskManagement);
+    }
+
     @AfterMethod
     public void tearDown() {
         if (atlasConfigurationMock != null) {
@@ -255,6 +286,54 @@ public class EntityDiscoveryServiceTest {
         }
     }
 
+    @Test
+    public void testAddSavedSearchWithGuidFromAnotherUserIsRejectedStrict() throws Exception {
+        EntityDiscoveryService strictService = createStrictServiceForSecurityTests();
+        AtlasUserSavedSearch   savedSearch   = new AtlasUserSavedSearch();
+
+        savedSearch.setGuid("testGuid");
+        savedSearch.setOwnerName("testUser");
+        savedSearch.setName("testSearch");
+        savedSearch.setSearchType(AtlasUserSavedSearch.SavedSearchType.BASIC);
+
+        AtlasUserSavedSearch existingSavedSearch = new AtlasUserSavedSearch();
+        existingSavedSearch.setGuid("testGuid");
+        existingSavedSearch.setOwnerName("otherUser");
+
+        when(userProfileService.getSavedSearch("testGuid")).thenReturn(existingSavedSearch);
+
+        try {
+            strictService.addSavedSearch("testUser", savedSearch);
+            fail("Expected cross-user GUID reuse to be rejected");
+        } catch (AtlasBaseException e) {
+            assertEquals(e.getAtlasErrorCode(), AtlasErrorCode.BAD_REQUEST);
+            verify(userProfileService, never()).addSavedSearch(any(AtlasUserSavedSearch.class));
+        }
+    }
+
+    @Test
+    public void testAddSavedSearchWithOwnGuidIsAllowedStrict() throws Exception {
+        EntityDiscoveryService strictService = createStrictServiceForSecurityTests();
+        AtlasUserSavedSearch   savedSearch   = new AtlasUserSavedSearch();
+
+        savedSearch.setGuid("testGuid");
+        savedSearch.setOwnerName("testUser");
+        savedSearch.setName("testSearch");
+        savedSearch.setSearchType(AtlasUserSavedSearch.SavedSearchType.BASIC);
+
+        AtlasUserSavedSearch existingSavedSearch = new AtlasUserSavedSearch();
+        existingSavedSearch.setGuid("testGuid");
+        existingSavedSearch.setOwnerName("testUser");
+
+        when(userProfileService.getSavedSearch("testGuid")).thenReturn(existingSavedSearch);
+        when(userProfileService.addSavedSearch(savedSearch)).thenReturn(savedSearch);
+
+        AtlasUserSavedSearch result = strictService.addSavedSearch("testUser", savedSearch);
+
+        assertEquals(result, savedSearch);
+        verify(userProfileService).addSavedSearch(savedSearch);
+    }
+
     @Test
     public void testDeleteSavedSearch() throws AtlasBaseException {
         try {