|
18 | 18 | package org.apache.atlas.discovery; |
19 | 19 |
|
20 | 20 | import org.apache.atlas.AtlasConfiguration; |
| 21 | +import org.apache.atlas.AtlasErrorCode; |
21 | 22 | import org.apache.atlas.RequestContext; |
22 | 23 | import org.apache.atlas.exception.AtlasBaseException; |
23 | 24 | import org.apache.atlas.model.discovery.AtlasQuickSearchResult; |
|
60 | 61 | import static org.mockito.ArgumentMatchers.anyString; |
61 | 62 | import static org.mockito.Mockito.mock; |
62 | 63 | import static org.mockito.Mockito.mockStatic; |
| 64 | +import static org.mockito.Mockito.never; |
| 65 | +import static org.mockito.Mockito.verify; |
63 | 66 | import static org.mockito.Mockito.when; |
| 67 | +import static org.testng.Assert.assertEquals; |
64 | 68 | import static org.testng.Assert.assertNotNull; |
65 | 69 | import static org.testng.Assert.assertTrue; |
| 70 | +import static org.testng.Assert.fail; |
66 | 71 |
|
67 | 72 | public class EntityDiscoveryServiceTest { |
68 | 73 | @Mock |
@@ -149,6 +154,32 @@ private void setupMockBehaviors() throws AtlasBaseException { |
149 | 154 | when(entityDiscoveryService.searchGUIDsWithParameters(any(AtlasAuditAgingType.class), any(Set.class), any(SearchParameters.class))).thenReturn(new HashSet<>()); |
150 | 155 | } |
151 | 156 |
|
| 157 | + private EntityDiscoveryService createStrictServiceForSecurityTests() throws Exception { |
| 158 | + /* |
| 159 | + * Strict setup path for security-sensitive tests: |
| 160 | + * always instantiate a real EntityDiscoveryService and never |
| 161 | + * fallback to a mocked/spied service. |
| 162 | + */ |
| 163 | + when(typeRegistry.getEntityTypeByName(anyString())).thenReturn(entityType); |
| 164 | + when(entityType.getTypeAndAllSubTypesQryStr()).thenReturn("(TestType)"); |
| 165 | + when(entityType.getAttribute(anyString())).thenReturn(attribute); |
| 166 | + when(entityType.getTypeName()).thenReturn("TestType"); |
| 167 | + when(attribute.getVertexPropertyName()).thenReturn("v.testAttr"); |
| 168 | + when(attribute.getAttributeType()).thenReturn(mock(org.apache.atlas.type.AtlasType.class)); |
| 169 | + when(indexer.getVertexIndexKeys()).thenReturn(Collections.emptySet()); |
| 170 | + when(indexer.getEdgeIndexKeys()).thenReturn(Collections.emptySet()); |
| 171 | + when(graph.indexQuery(anyString(), anyString())).thenReturn(indexQuery); |
| 172 | + when(graph.query()).thenReturn(mock(org.apache.atlas.repository.graphdb.AtlasGraphQuery.class)); |
| 173 | + when(indexQuery.vertices()).thenReturn(Collections.emptyIterator()); |
| 174 | + when(indexQuery.vertexTotals()).thenReturn(0L); |
| 175 | + |
| 176 | + RequestContext context = mock(RequestContext.class); |
| 177 | + when(RequestContext.get()).thenReturn(context); |
| 178 | + when(context.getUser()).thenReturn("testUser"); |
| 179 | + |
| 180 | + return new EntityDiscoveryService(typeRegistry, graph, indexer, searchTracker, userProfileService, taskManagement); |
| 181 | + } |
| 182 | + |
152 | 183 | @AfterMethod |
153 | 184 | public void tearDown() { |
154 | 185 | if (atlasConfigurationMock != null) { |
@@ -255,6 +286,54 @@ public void testUpdateSavedSearch() throws AtlasBaseException { |
255 | 286 | } |
256 | 287 | } |
257 | 288 |
|
| 289 | + @Test |
| 290 | + public void testAddSavedSearchWithGuidFromAnotherUserIsRejectedStrict() throws Exception { |
| 291 | + EntityDiscoveryService strictService = createStrictServiceForSecurityTests(); |
| 292 | + AtlasUserSavedSearch savedSearch = new AtlasUserSavedSearch(); |
| 293 | + |
| 294 | + savedSearch.setGuid("testGuid"); |
| 295 | + savedSearch.setOwnerName("testUser"); |
| 296 | + savedSearch.setName("testSearch"); |
| 297 | + savedSearch.setSearchType(AtlasUserSavedSearch.SavedSearchType.BASIC); |
| 298 | + |
| 299 | + AtlasUserSavedSearch existingSavedSearch = new AtlasUserSavedSearch(); |
| 300 | + existingSavedSearch.setGuid("testGuid"); |
| 301 | + existingSavedSearch.setOwnerName("otherUser"); |
| 302 | + |
| 303 | + when(userProfileService.getSavedSearch("testGuid")).thenReturn(existingSavedSearch); |
| 304 | + |
| 305 | + try { |
| 306 | + strictService.addSavedSearch("testUser", savedSearch); |
| 307 | + fail("Expected cross-user GUID reuse to be rejected"); |
| 308 | + } catch (AtlasBaseException e) { |
| 309 | + assertEquals(e.getAtlasErrorCode(), AtlasErrorCode.BAD_REQUEST); |
| 310 | + verify(userProfileService, never()).addSavedSearch(any(AtlasUserSavedSearch.class)); |
| 311 | + } |
| 312 | + } |
| 313 | + |
| 314 | + @Test |
| 315 | + public void testAddSavedSearchWithOwnGuidIsAllowedStrict() throws Exception { |
| 316 | + EntityDiscoveryService strictService = createStrictServiceForSecurityTests(); |
| 317 | + AtlasUserSavedSearch savedSearch = new AtlasUserSavedSearch(); |
| 318 | + |
| 319 | + savedSearch.setGuid("testGuid"); |
| 320 | + savedSearch.setOwnerName("testUser"); |
| 321 | + savedSearch.setName("testSearch"); |
| 322 | + savedSearch.setSearchType(AtlasUserSavedSearch.SavedSearchType.BASIC); |
| 323 | + |
| 324 | + AtlasUserSavedSearch existingSavedSearch = new AtlasUserSavedSearch(); |
| 325 | + existingSavedSearch.setGuid("testGuid"); |
| 326 | + existingSavedSearch.setOwnerName("testUser"); |
| 327 | + |
| 328 | + when(userProfileService.getSavedSearch("testGuid")).thenReturn(existingSavedSearch); |
| 329 | + when(userProfileService.addSavedSearch(savedSearch)).thenReturn(savedSearch); |
| 330 | + |
| 331 | + AtlasUserSavedSearch result = strictService.addSavedSearch("testUser", savedSearch); |
| 332 | + |
| 333 | + assertEquals(result, savedSearch); |
| 334 | + verify(userProfileService).addSavedSearch(savedSearch); |
| 335 | + } |
| 336 | + |
258 | 337 | @Test |
259 | 338 | public void testDeleteSavedSearch() throws AtlasBaseException { |
260 | 339 | try { |
|
0 commit comments