This is an automated email from the ASF dual-hosted git repository.
chaitalicod pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/atlas-2.6 by this push:
new 0f4d63d4e ATLAS-5372: Prevent cross-user overwrite in saved search
create API via caller-supplied guid (#724) (Cherrypicked from
c1cc1ff9114f19248ec642d61eb861a1dab75919) Co-authored-by: chaitalithombare
<[email protected]>
0f4d63d4e is described below
commit 0f4d63d4e4efc855444908e6a4ee6c5fb5f35236
Author: chaitalicod <[email protected]>
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)
(Cherrypicked from c1cc1ff9114f19248ec642d61eb861a1dab75919)
Co-authored-by: chaitalithombare <[email protected]>
---
.../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 {