You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2023/05/01 08:00:37 UTC

[ranger] branch ranger-2.4 updated: RANGER-4175: REST API to find security-zone for a given resource

This is an automated email from the ASF dual-hosted git repository.

madhan pushed a commit to branch ranger-2.4
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/ranger-2.4 by this push:
     new d5b67652a RANGER-4175: REST API to find security-zone for a given resource
d5b67652a is described below

commit d5b67652ac014705a784b549d089e0a351dc1b9b
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Wed Apr 5 15:33:57 2023 -0700

    RANGER-4175: REST API to find security-zone for a given resource
    
    (cherry picked from commit afb8fe95c28f53cc81631f02f94c200875afee9e)
---
 .../ranger/plugin/policyengine/PolicyEngine.java   |  4 +++
 .../main/java/org/apache/ranger/RangerClient.java  |  6 ++++
 .../python/apache_ranger/client/ranger_client.py   | 35 ++++++++++++----------
 intg/src/main/python/apache_ranger/utils.py        | 13 +++++++-
 .../java/org/apache/ranger/TestRangerClient.java   | 30 +++++++++++++++++++
 .../org/apache/ranger/biz/RangerPolicyAdmin.java   |  3 ++
 .../apache/ranger/biz/RangerPolicyAdminImpl.java   | 25 ++++++++++++++++
 .../java/org/apache/ranger/rest/PublicAPIsv2.java  |  9 ++++++
 .../org/apache/ranger/rest/SecurityZoneREST.java   | 33 ++++++++++++++++++++
 .../java/org/apache/ranger/rest/ServiceREST.java   |  2 +-
 10 files changed, 143 insertions(+), 17 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java
index 3864f30d2..cebdb9cea 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java
@@ -362,6 +362,10 @@ public class PolicyEngine {
         return ret;
     }
 
+    public Set<String> getMatchedZonesForResourceAndChildren(Map<String, ?> resource) {
+        return getMatchedZonesForResourceAndChildren(convertToAccessResource(resource));
+    }
+
     public Set<String> getMatchedZonesForResourceAndChildren(RangerAccessResource accessResource) {
         if (LOG.isDebugEnabled()) {
             LOG.debug("==> PolicyEngine.getMatchedZonesForResourceAndChildren(" + accessResource + ")");
diff --git a/intg/src/main/java/org/apache/ranger/RangerClient.java b/intg/src/main/java/org/apache/ranger/RangerClient.java
index 92149c9e0..838dfa52c 100644
--- a/intg/src/main/java/org/apache/ranger/RangerClient.java
+++ b/intg/src/main/java/org/apache/ranger/RangerClient.java
@@ -82,6 +82,7 @@ public class RangerClient {
     private static final String URI_ZONE_BY_NAME          = URI_ZONE + "/name/%s";
     private static final String URI_ZONE_HEADERS          = URI_BASE + "/zone-headers";
     private static final String URI_ZONE_SERVICE_HEADERS  = URI_ZONE + "/%d/service-headers";
+    private static final String URI_ZONE_NAMES_FOR_RES    = URI_BASE + "/zone-names/%s/resource";
 
     private static final String URI_SERVICE_TAGS          = URI_SERVICE + "/%s/tags";
     private static final String URI_PLUGIN_INFO           = URI_BASE + "/plugins/info";
@@ -126,6 +127,7 @@ public class RangerClient {
     public static final API GET_ZONE_BY_NAME         = new API(URI_ZONE_BY_NAME, HttpMethod.GET, Response.Status.OK);
     public static final API GET_ZONE_HEADERS         = new API(URI_ZONE_HEADERS, HttpMethod.GET, Response.Status.OK);
     public static final API GET_ZONE_SERVICE_HEADERS = new API(URI_ZONE_SERVICE_HEADERS, HttpMethod.GET, Response.Status.OK);
+    public static final API GET_ZONE_NAMES_FOR_RES   = new API(URI_ZONE_NAMES_FOR_RES, HttpMethod.GET, Response.Status.OK);
     public static final API FIND_ZONES               = new API(URI_ZONE, HttpMethod.GET, Response.Status.OK);
 
     public static final API CREATE_ROLE         = new API(URI_ROLE, HttpMethod.POST, Response.Status.OK);
@@ -359,6 +361,10 @@ public class RangerClient {
         return callAPI(GET_ZONE_SERVICE_HEADERS, filter, null, new GenericType<List<RangerServiceHeaderInfo>>(){});
     }
 
+    public Set<String> getSecurityZoneNamesForResource(String serviceName, Map<String, String> resource) throws RangerServiceException {
+        return callAPI(GET_ZONE_NAMES_FOR_RES.applyUrlFormat(serviceName), resource, null, new GenericType<Set<String>>(){});
+    }
+
     public List<RangerSecurityZone> findSecurityZones(Map<String, String> filter) throws RangerServiceException {
         return callAPI(FIND_ZONES, filter, null, new GenericType<List<RangerSecurityZone>>(){});
     }
diff --git a/intg/src/main/python/apache_ranger/client/ranger_client.py b/intg/src/main/python/apache_ranger/client/ranger_client.py
index 4680e2d8e..a544ccc69 100644
--- a/intg/src/main/python/apache_ranger/client/ranger_client.py
+++ b/intg/src/main/python/apache_ranger/client/ranger_client.py
@@ -220,6 +220,9 @@ class RangerClient:
 
       return type_coerce_list(resp, RangerServiceHeaderInfo)
 
+    def get_zone_names_for_resource(self, serviceName, resource):
+      return self.client_http.call_api(RangerClient.GET_ZONE_NAMES_FOR_RESOURCE.format_path({ 'serviceName': serviceName }), query_params=resource_to_query_params(resource))
+
     def find_security_zones(self, filter=None):
         resp = self.client_http.call_api(RangerClient.FIND_ZONES, filter)
 
@@ -325,11 +328,12 @@ class RangerClient:
     URI_GRANT_ROLE          = URI_ROLE + "/grant/{name}"
     URI_REVOKE_ROLE         = URI_ROLE + "/revoke/{name}"
 
-    URI_ZONE                 = URI_BASE + "/zones"
-    URI_ZONE_BY_ID           = URI_ZONE + "/{id}"
-    URI_ZONE_BY_NAME         = URI_ZONE + "/name/{name}"
-    URI_ZONE_HEADERS         = URI_BASE + "/zone-headers"
-    URI_ZONE_SERVICE_HEADERS = URI_ZONE + "/{id}/service-headers"
+    URI_ZONE                    = URI_BASE + "/zones"
+    URI_ZONE_BY_ID              = URI_ZONE + "/{id}"
+    URI_ZONE_BY_NAME            = URI_ZONE + "/name/{name}"
+    URI_ZONE_HEADERS            = URI_BASE + "/zone-headers"
+    URI_ZONE_SERVICE_HEADERS    = URI_ZONE + "/{id}/service-headers"
+    URI_ZONE_NAMES_FOR_RESOURCE = URI_BASE + "/zone-names/{serviceName}/resource"
 
     URI_SERVICE_TAGS        = URI_SERVICE + "/{serviceName}/tags"
     URI_PLUGIN_INFO         = URI_BASE + "/plugins/info"
@@ -365,16 +369,17 @@ class RangerClient:
     GET_POLICIES_IN_SERVICE   = API(URI_POLICIES_IN_SERVICE, HttpMethod.GET, HTTPStatus.OK)
     FIND_POLICIES             = API(URI_POLICY, HttpMethod.GET, HTTPStatus.OK)
 
-    CREATE_ZONE               = API(URI_ZONE, HttpMethod.POST, HTTPStatus.OK)
-    UPDATE_ZONE_BY_ID         = API(URI_ZONE_BY_ID, HttpMethod.PUT, HTTPStatus.OK)
-    UPDATE_ZONE_BY_NAME       = API(URI_ZONE_BY_NAME, HttpMethod.PUT, HTTPStatus.OK)
-    DELETE_ZONE_BY_ID         = API(URI_ZONE_BY_ID, HttpMethod.DELETE, HTTPStatus.NO_CONTENT)
-    DELETE_ZONE_BY_NAME       = API(URI_ZONE_BY_NAME, HttpMethod.DELETE, HTTPStatus.NO_CONTENT)
-    GET_ZONE_BY_ID            = API(URI_ZONE_BY_ID, HttpMethod.GET, HTTPStatus.OK)
-    GET_ZONE_BY_NAME          = API(URI_ZONE_BY_NAME, HttpMethod.GET, HTTPStatus.OK)
-    FIND_ZONES                = API(URI_ZONE, HttpMethod.GET, HTTPStatus.OK)
-    GET_ZONE_HEADERS          = API(URI_ZONE_HEADERS, HttpMethod.GET, HTTPStatus.OK)
-    GET_ZONE_SERVICE_HEADERS  = API(URI_ZONE_SERVICE_HEADERS, HttpMethod.GET, HTTPStatus.OK)
+    CREATE_ZONE                 = API(URI_ZONE, HttpMethod.POST, HTTPStatus.OK)
+    UPDATE_ZONE_BY_ID           = API(URI_ZONE_BY_ID, HttpMethod.PUT, HTTPStatus.OK)
+    UPDATE_ZONE_BY_NAME         = API(URI_ZONE_BY_NAME, HttpMethod.PUT, HTTPStatus.OK)
+    DELETE_ZONE_BY_ID           = API(URI_ZONE_BY_ID, HttpMethod.DELETE, HTTPStatus.NO_CONTENT)
+    DELETE_ZONE_BY_NAME         = API(URI_ZONE_BY_NAME, HttpMethod.DELETE, HTTPStatus.NO_CONTENT)
+    GET_ZONE_BY_ID              = API(URI_ZONE_BY_ID, HttpMethod.GET, HTTPStatus.OK)
+    GET_ZONE_BY_NAME            = API(URI_ZONE_BY_NAME, HttpMethod.GET, HTTPStatus.OK)
+    FIND_ZONES                  = API(URI_ZONE, HttpMethod.GET, HTTPStatus.OK)
+    GET_ZONE_HEADERS            = API(URI_ZONE_HEADERS, HttpMethod.GET, HTTPStatus.OK)
+    GET_ZONE_SERVICE_HEADERS    = API(URI_ZONE_SERVICE_HEADERS, HttpMethod.GET, HTTPStatus.OK)
+    GET_ZONE_NAMES_FOR_RESOURCE = API(URI_ZONE_NAMES_FOR_RESOURCE, HttpMethod.GET, HTTPStatus.OK)
 
     CREATE_ROLE               = API(URI_ROLE, HttpMethod.POST, HTTPStatus.OK)
     UPDATE_ROLE_BY_ID         = API(URI_ROLE_BY_ID, HttpMethod.PUT, HTTPStatus.OK)
diff --git a/intg/src/main/python/apache_ranger/utils.py b/intg/src/main/python/apache_ranger/utils.py
index 28e0e4b60..2611a1bf0 100644
--- a/intg/src/main/python/apache_ranger/utils.py
+++ b/intg/src/main/python/apache_ranger/utils.py
@@ -18,7 +18,8 @@
 
 import enum
 
-APPLICATION_JSON         = 'application/json'
+APPLICATION_JSON            = 'application/json'
+QUERY_PARAM_PREFIX_RESOURCE = 'resource:'
 
 
 def non_null(obj, defValue):
@@ -62,6 +63,16 @@ def type_coerce_dict_list(obj, objType):
 
     return ret
 
+def resource_to_query_params(resource, query_params=None):
+    if isinstance(resource, dict):
+      if query_params is None:
+        query_params = {}
+
+      for key, value in resource.items():
+        query_params[QUERY_PARAM_PREFIX_RESOURCE + key] = value
+
+    return query_params
+
 def type_coerce_list_dict(obj, objType):
     if isinstance(obj, list):
         return [ type_coerce_dict(entry, objType) for entry in obj ]
diff --git a/intg/src/test/java/org/apache/ranger/TestRangerClient.java b/intg/src/test/java/org/apache/ranger/TestRangerClient.java
index 054881f4b..c63b3ab20 100644
--- a/intg/src/test/java/org/apache/ranger/TestRangerClient.java
+++ b/intg/src/test/java/org/apache/ranger/TestRangerClient.java
@@ -19,6 +19,8 @@
 package org.apache.ranger;
 
 import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+import org.apache.ranger.plugin.model.RangerSecurityZone;
 import org.apache.ranger.plugin.model.RangerSecurityZoneHeaderInfo;
 import org.apache.ranger.plugin.model.RangerService;
 import org.apache.ranger.plugin.model.RangerServiceHeaderInfo;
@@ -173,4 +175,32 @@ public class TestRangerClient {
 
         Assert.assertEquals(Collections.emptyList(), serviceHeaders);
     }
+
+    @Test
+    public void testGetSecurityZoneNamesForResource() throws RangerServiceException {
+        RangerClient        client      = Mockito.mock(RangerClient.class);
+        String              serviceName = "dev_hive";
+        Map<String, String> resource    = new HashMap<String, String>() {{
+                                                put("database", "testdb");
+                                                put("table", "testtbl1");
+                                            }};
+
+        when(client.getSecurityZoneNamesForResource(serviceName, resource)).thenReturn(Collections.emptySet());
+
+        Set<String> zoneNames = client.getSecurityZoneNamesForResource(serviceName, resource);
+
+        Assert.assertEquals(Collections.emptySet(), zoneNames);
+    }
+
+    @Test
+    public void testFindSecurityZones() throws RangerServiceException {
+        RangerClient        client = Mockito.mock(RangerClient.class);
+        Map<String, String> filter = Collections.emptyMap();
+
+        when(client.findSecurityZones(filter)).thenReturn(Collections.emptyList());
+
+        List<RangerSecurityZone> securityZones = client.findSecurityZones(filter);
+
+        Assert.assertEquals(Collections.emptyList(), securityZones);
+    }
 }
\ No newline at end of file
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdmin.java b/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdmin.java
index f975287f9..15a1e7118 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdmin.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdmin.java
@@ -19,6 +19,7 @@
 
 package org.apache.ranger.biz;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -57,6 +58,8 @@ public interface RangerPolicyAdmin {
 
     Set<String> getRolesFromUserAndGroups(String user, Set<String> groups);
 
+    Collection<String> getZoneNamesForResource(Map<String, ?> resource);
+
     String getUniquelyMatchedZoneName(GrantRevokeRequest grantRevokeRequest);
 
     // This API is used only by test-code
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdminImpl.java b/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdminImpl.java
index 97a384f30..6799be200 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdminImpl.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyAdminImpl.java
@@ -521,6 +521,31 @@ public class RangerPolicyAdminImpl implements RangerPolicyAdmin {
         return ret;
     }
 
+    @Override
+    public Collection<String> getZoneNamesForResource(Map<String, ?> resource) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerPolicyAdminImpl.getSecurityZonesForResource(" + resource + ")");
+        }
+
+        Collection<String> ret = null;
+
+        try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) {
+            if (LOG.isDebugEnabled()) {
+                if (readLock.isLockingEnabled()) {
+                    LOG.debug("Acquired lock - " + readLock);
+                }
+            }
+
+            ret = policyEngine.getMatchedZonesForResourceAndChildren(resource);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerPolicyAdminImpl.getSecurityZonesForResource(" + resource + ") : " + ret);
+        }
+
+        return ret;
+    }
+
     @Override
     public String getUniquelyMatchedZoneName(GrantRevokeRequest grantRevokeRequest) {
         if (LOG.isDebugEnabled()) {
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java b/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java
index 85cd7dd67..7f00e9dcb 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java
@@ -60,6 +60,7 @@ import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Context;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 @Path("public/v2")
@@ -205,6 +206,14 @@ public class PublicAPIsv2 {
         return ret;
     }
 
+	@GET
+	@Path("/api/zone-names/{serviceName}/resource")
+	@Produces({ "application/json" })
+	@PreAuthorize("@rangerPreAuthSecurityHandler.isAPISpnegoAccessible()")
+	public Collection<String> getSecurityZoneNamesForResource(@PathParam("serviceName") String serviceName, @Context HttpServletRequest request) {
+		return securityZoneRest.getZoneNamesForResource(serviceName, request);
+	}
+
 	/*
 	* ServiceDef Manipulation APIs
 	 */
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java b/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java
index e35dc12cc..54c8b22f0 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java
@@ -20,6 +20,8 @@
 package org.apache.ranger.rest;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -41,6 +43,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Context;
 
+import org.apache.ranger.biz.RangerPolicyAdmin;
 import org.apache.ranger.biz.RangerBizUtil;
 import org.apache.ranger.biz.SecurityZoneDBStore;
 import org.apache.ranger.biz.ServiceDBStore;
@@ -326,6 +329,36 @@ public class SecurityZoneREST {
         return ret;
     }
 
+    @GET
+    @Path("/zone-names/{serviceName}/resource")
+    @Produces({ "application/json" })
+    public Collection<String> getZoneNamesForResource(@PathParam("serviceName") String serviceName, @Context HttpServletRequest request) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> SecurityZoneREST.getZoneNamesForResource(" + serviceName + ")");
+        }
+
+        if (!serviceRest.isServiceAdmin(serviceName)) {
+            throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN,
+                    "User '" + bizUtil.getCurrentUserLoginId() + "' does not have privilege", true);
+        }
+
+        Collection<String> ret         = null;
+        RangerPolicyAdmin  policyAdmin = serviceRest.getPolicyAdminForDelegatedAdmin(serviceName);
+
+        if (policyAdmin != null) {
+            SearchFilter        filter   = searchUtil.getSearchFilter(request, Collections.emptyList());
+            Map<String, String> resource = filter.getParamsWithPrefix(SearchFilter.RESOURCE_PREFIX, true);
+
+            ret = policyAdmin.getZoneNamesForResource(resource);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== SecurityZoneREST.getZoneNamesForResource(" + serviceName + "): ret=" + ret);
+        }
+
+        return ret;
+    }
+
 	private void ensureAdminAccess(){
 		if(!bizUtil.isAdmin()){
 			String userName = bizUtil.getCurrentUserLoginId();
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
index e11d8efd8..2b4acbcbf 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
@@ -4534,7 +4534,7 @@ public class ServiceREST {
 		return deletedServiceName;
 	}
 
-	private boolean isServiceAdmin(String serviceName) {
+	boolean isServiceAdmin(String serviceName) {
 		boolean ret = bizUtil.isAdmin();
 
 		if (!ret && StringUtils.isNotEmpty(serviceName)) {