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 {