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