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 027fa9bbe ATLAS-5414: Enforce authorization on AdminResource active 
search list and cancel endpoints (#765)
027fa9bbe is described below

commit 027fa9bbea349203292d008446bda7ab4e41c43a
Author: chaitalicod <[email protected]>
AuthorDate: Thu Sep 24 09:48:36 2026 +0530

    ATLAS-5414: Enforce authorization on AdminResource active search list and 
cancel endpoints (#765)
    
    Co-authored-by: [email protected] <[email protected]>
---
 .../java/org/apache/atlas/util/SearchTracker.java  | 34 +++++++++---
 .../org/apache/atlas/util/SearchTrackerTest.java   | 63 ++++++++++++++++++++++
 .../apache/atlas/web/resources/AdminResource.java  | 21 +++++++-
 .../atlas/web/resources/AdminResourceTest.java     | 44 ++++++++++-----
 4 files changed, 141 insertions(+), 21 deletions(-)

diff --git a/repository/src/main/java/org/apache/atlas/util/SearchTracker.java 
b/repository/src/main/java/org/apache/atlas/util/SearchTracker.java
index fd01eaded..ace05d59a 100644
--- a/repository/src/main/java/org/apache/atlas/util/SearchTracker.java
+++ b/repository/src/main/java/org/apache/atlas/util/SearchTracker.java
@@ -17,8 +17,10 @@
  */
 package org.apache.atlas.util;
 
+import org.apache.atlas.RequestContext;
 import org.apache.atlas.annotation.AtlasService;
 import org.apache.atlas.discovery.SearchContext;
+import org.apache.commons.lang3.StringUtils;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -26,7 +28,7 @@ import java.util.Set;
 
 @AtlasService
 public class SearchTracker {
-    private final Map<String, SearchContext> activeSearches = new HashMap<>();
+    private final Map<String, ActiveSearch> activeSearches = new HashMap<>();
 
     /**
      * @param context
@@ -34,7 +36,7 @@ public class SearchTracker {
     public String add(SearchContext context) {
         String searchId = Thread.currentThread().getName();
 
-        activeSearches.put(searchId, context);
+        activeSearches.put(searchId, new ActiveSearch(context, 
RequestContext.getCurrentUser()));
 
         return searchId;
     }
@@ -46,19 +48,27 @@ public class SearchTracker {
     public SearchContext terminate(String searchId) {
         SearchContext ret = null;
 
-        if (activeSearches.containsKey(searchId)) {
-            SearchContext pipelineToTerminate = 
activeSearches.remove(searchId);
+        ActiveSearch activeSearch = activeSearches.remove(searchId);
 
-            pipelineToTerminate.terminateSearch(true);
+        if (activeSearch != null) {
+            activeSearch.context.terminateSearch(true);
 
-            ret = pipelineToTerminate;
+            ret = activeSearch.context;
         }
 
         return ret;
     }
 
     public SearchContext remove(String id) {
-        return activeSearches.remove(id);
+        ActiveSearch activeSearch = activeSearches.remove(id);
+
+        return activeSearch != null ? activeSearch.context : null;
+    }
+
+    public String getSearchOwner(String searchId) {
+        ActiveSearch activeSearch = activeSearches.get(searchId);
+
+        return activeSearch != null ? activeSearch.userName : null;
     }
 
     /**
@@ -67,4 +77,14 @@ public class SearchTracker {
     public Set<String> getActiveSearches() {
         return activeSearches.keySet();
     }
+
+    private static final class ActiveSearch {
+        private final SearchContext context;
+        private final String        userName;
+
+        private ActiveSearch(SearchContext context, String userName) {
+            this.context  = context;
+            this.userName = StringUtils.defaultString(userName);
+        }
+    }
 }
diff --git 
a/repository/src/test/java/org/apache/atlas/util/SearchTrackerTest.java 
b/repository/src/test/java/org/apache/atlas/util/SearchTrackerTest.java
new file mode 100644
index 000000000..fe7db70f9
--- /dev/null
+++ b/repository/src/test/java/org/apache/atlas/util/SearchTrackerTest.java
@@ -0,0 +1,63 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.atlas.util;
+
+import org.apache.atlas.RequestContext;
+import org.apache.atlas.discovery.SearchContext;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+public class SearchTrackerTest {
+    @AfterMethod
+    public void tearDown() {
+        RequestContext.clear();
+    }
+
+    @Test
+    public void testAddRecordsSearchOwner() {
+        SearchTracker searchTracker = new SearchTracker();
+        SearchContext searchContext = mock(SearchContext.class);
+
+        RequestContext.get().setUser("alice", null);
+
+        String searchId = searchTracker.add(searchContext);
+
+        assertNotNull(searchId);
+        assertEquals(searchTracker.getSearchOwner(searchId), "alice");
+        assertTrue(searchTracker.getActiveSearches().contains(searchId));
+    }
+
+    @Test
+    public void testTerminateRemovesActiveSearch() {
+        SearchTracker searchTracker = new SearchTracker();
+        SearchContext searchContext = mock(SearchContext.class);
+
+        RequestContext.get().setUser("alice", null);
+
+        String searchId = searchTracker.add(searchContext);
+
+        assertNotNull(searchTracker.terminate(searchId));
+        assertNull(searchTracker.getSearchOwner(searchId));
+    }
+}
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java 
b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java
index 266f75c41..b7a92a639 100755
--- a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java
+++ b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java
@@ -1175,14 +1175,31 @@ public class AdminResource {
     @GET
     @Path("activeSearches")
     @Produces(Servlets.JSON_MEDIA_TYPE)
-    public Set<String> getActiveSearches() {
+    public Set<String> getActiveSearches() throws AtlasBaseException {
+        AtlasAuthorizationUtils.verifyAccess(new 
AtlasAdminAccessRequest(AtlasPrivilege.ADMIN_EXPORT), "active searches");
+
         return activeSearches.getActiveSearches();
     }
 
     @DELETE
     @Path("activeSearches/{id}")
     @Produces(Servlets.JSON_MEDIA_TYPE)
-    public boolean terminateActiveSearch(@PathParam("id") String searchId) {
+    public boolean terminateActiveSearch(@PathParam("id") String searchId) 
throws AtlasBaseException {
+        String  currentUser = AtlasAuthorizationUtils.getCurrentUserName();
+        boolean isAdmin     = AtlasAuthorizationUtils.isAccessAllowed(new 
AtlasAdminAccessRequest(AtlasPrivilege.ADMIN_EXPORT));
+
+        if (!isAdmin) {
+            String searchOwner = activeSearches.getSearchOwner(searchId);
+
+            if (searchOwner == null) {
+                return false;
+            }
+
+            if (!StringUtils.equals(currentUser, searchOwner)) {
+                AtlasAuthorizationUtils.verifyAccess(new 
AtlasAdminAccessRequest(AtlasPrivilege.ADMIN_EXPORT), "terminate active 
search");
+            }
+        }
+
         SearchContext terminate = activeSearches.terminate(searchId);
 
         return null != terminate;
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java 
b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java
index 06d20676a..fab6f144a 100644
--- a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java
+++ b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java
@@ -246,6 +246,7 @@ public class AdminResourceTest {
         try (MockedStatic<AtlasAuthorizationUtils> mockedUtils = 
mockStatic(AtlasAuthorizationUtils.class)) {
             mockedUtils.when(() -> 
AtlasAuthorizationUtils.verifyAccess(any(org.apache.atlas.authorize.AtlasAdminAccessRequest.class),
 any())).then(invocation -> null);
             mockedUtils.when(() -> 
AtlasAuthorizationUtils.verifyAccess(any(AtlasEntityAccessRequest.class), 
any())).then(invocation -> null);
+            mockedUtils.when(() -> 
AtlasAuthorizationUtils.isAccessAllowed(any(org.apache.atlas.authorize.AtlasAdminAccessRequest.class))).thenReturn(true);
             runnable.run();
         }
     }
@@ -254,6 +255,7 @@ public class AdminResourceTest {
         try (MockedStatic<AtlasAuthorizationUtils> mockedUtils = 
mockStatic(AtlasAuthorizationUtils.class)) {
             mockedUtils.when(() -> 
AtlasAuthorizationUtils.verifyAccess(any(org.apache.atlas.authorize.AtlasAdminAccessRequest.class),
 any())).then(invocation -> null);
             mockedUtils.when(() -> 
AtlasAuthorizationUtils.verifyAccess(any(AtlasEntityAccessRequest.class), 
any())).then(invocation -> null);
+            mockedUtils.when(() -> 
AtlasAuthorizationUtils.isAccessAllowed(any(org.apache.atlas.authorize.AtlasAdminAccessRequest.class))).thenReturn(true);
             return callable.call();
         }
     }
@@ -567,13 +569,19 @@ public class AdminResourceTest {
 
         AdminResource adminResource = createAdminResource();
 
-        Set<String> result = adminResource.getActiveSearches();
+        withAuthorizationBypass(() -> {
+            try {
+                Set<String> result = adminResource.getActiveSearches();
 
-        assertNotNull(result);
-        assertEquals(result.size(), 2);
-        assertTrue(result.contains("search1"));
-        assertTrue(result.contains("search2"));
-        verify(activeSearches).getActiveSearches();
+                assertNotNull(result);
+                assertEquals(result.size(), 2);
+                assertTrue(result.contains("search1"));
+                assertTrue(result.contains("search2"));
+                verify(activeSearches).getActiveSearches();
+            } catch (AtlasBaseException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 
     @Test
@@ -585,10 +593,16 @@ public class AdminResourceTest {
 
         AdminResource adminResource = createAdminResource();
 
-        boolean result = adminResource.terminateActiveSearch(searchId);
+        withAuthorizationBypass(() -> {
+            try {
+                boolean result = adminResource.terminateActiveSearch(searchId);
 
-        assertTrue(result);
-        verify(activeSearches).terminate(searchId);
+                assertTrue(result);
+                verify(activeSearches).terminate(searchId);
+            } catch (AtlasBaseException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 
     @Test
@@ -599,10 +613,16 @@ public class AdminResourceTest {
 
         AdminResource adminResource = createAdminResource();
 
-        boolean result = adminResource.terminateActiveSearch(searchId);
+        withAuthorizationBypass(() -> {
+            try {
+                boolean result = adminResource.terminateActiveSearch(searchId);
 
-        assertFalse(result);
-        verify(activeSearches).terminate(searchId);
+                assertFalse(result);
+                verify(activeSearches).terminate(searchId);
+            } catch (AtlasBaseException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 
     @Test

Reply via email to