diff --git a/security-admin/src/main/java/org/apache/ranger/rest/GdsREST.java b/security-admin/src/main/java/org/apache/ranger/rest/GdsREST.java index f54d058c1c..a46db7bb1f 100755 --- a/security-admin/src/main/java/org/apache/ranger/rest/GdsREST.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/GdsREST.java @@ -1261,6 +1261,63 @@ public RangerSharedResource updateSharedResource(@PathParam("id") Long resourceI return ret; } + @PUT + @Path("/resources") + @Consumes("application/json") + @Produces("application/json") + @PreAuthorize("@rangerPreAuthSecurityHandler.isAPIAccessible(\"" + RangerAPIList.UPDATE_SHARED_RESOURCES + "\")") + public void updateSharedResources(@QueryParam("forceDelete") @DefaultValue("false") boolean forceDelete, List resources) { + LOG.debug("==> GdsREST.updateSharedResources(resources={}, forceDelete={})", resources, forceDelete); + + RangerPerfTracer perf = null; + + try { + if (resources == null) { + throw new Exception("resources must not be null"); + } + + if (resources.size() > SHARED_RESOURCES_MAX_BATCH_SIZE) { + throw new Exception("updateSharedResources batch size exceeded the configured limit: Maximum allowed is " + SHARED_RESOURCES_MAX_BATCH_SIZE); + } + + if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "GdsREST.updateSharedResources(" + resources + ",forceDelete=" + forceDelete + ")"); + } + + if (forceDelete) { + List resourceIds = new ArrayList<>(); + + for (RangerSharedResource resource : resources) { + if (resource == null || resource.getId() == null) { + throw new Exception("resource id must not be null for forceDelete"); + } + + resourceIds.add(resource.getId()); + } + + gdsStore.removeSharedResources(resourceIds); + } else { + for (RangerSharedResource resource : resources) { + if (resource == null) { + throw new Exception("resource must not be null"); + } + + gdsStore.updateSharedResource(resource); + } + } + } catch (WebApplicationException excp) { + throw excp; + } catch (Throwable excp) { + LOG.error("updateSharedResources(resources={}, forceDelete={}) failed", resources, forceDelete, excp); + + throw restErrorUtil.createRESTException(excp.getMessage()); + } finally { + RangerPerfTracer.log(perf); + } + + LOG.debug("<== GdsREST.updateSharedResources(resources={}, forceDelete={})", resources, forceDelete); + } + @DELETE @Path("/resource/{id}") @Produces("application/json") diff --git a/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java b/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java index 27d076a480..7eedad6f27 100755 --- a/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java +++ b/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java @@ -237,6 +237,7 @@ public class RangerAPIList { public static final String ADD_SHARED_RESOURCE = "GdsREST.addSharedResource"; public static final String ADD_SHARED_RESOURCES = "GdsREST.addSharedResources"; public static final String UPDATE_SHARED_RESOURCE = "GdsREST.updateSharedResource"; + public static final String UPDATE_SHARED_RESOURCES = "GdsREST.updateSharedResources"; public static final String REMOVE_SHARED_RESOURCE = "GdsREST.removeSharedResource"; public static final String REMOVE_SHARED_RESOURCES = "GdsREST.removeSharedResources"; public static final String GET_SHARED_RESOURCE = "GdsREST.getSharedResource"; diff --git a/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIMapping.java b/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIMapping.java index 1c4ee11837..f41585cdfa 100644 --- a/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIMapping.java +++ b/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIMapping.java @@ -547,6 +547,7 @@ private void mapGDSWithAPIs() { apiAssociatedWithGDS.add(RangerAPIList.ADD_SHARED_RESOURCE); apiAssociatedWithGDS.add(RangerAPIList.ADD_SHARED_RESOURCES); apiAssociatedWithGDS.add(RangerAPIList.UPDATE_SHARED_RESOURCE); + apiAssociatedWithGDS.add(RangerAPIList.UPDATE_SHARED_RESOURCES); apiAssociatedWithGDS.add(RangerAPIList.REMOVE_SHARED_RESOURCE); apiAssociatedWithGDS.add(RangerAPIList.REMOVE_SHARED_RESOURCES); apiAssociatedWithGDS.add(RangerAPIList.GET_SHARED_RESOURCE); diff --git a/security-admin/src/test/java/org/apache/ranger/rest/TestGdsREST.java b/security-admin/src/test/java/org/apache/ranger/rest/TestGdsREST.java index 3f4982b38f..a4e121291e 100644 --- a/security-admin/src/test/java/org/apache/ranger/rest/TestGdsREST.java +++ b/security-admin/src/test/java/org/apache/ranger/rest/TestGdsREST.java @@ -924,6 +924,59 @@ public void testRemoveSharedResource() { verify(gdsStore).removeSharedResources(Arrays.asList(resourceId)); } + @Test + public void testUpdateSharedResources_updatesWhenNotForceDelete() throws Exception { + RangerGds.RangerSharedResource resource1 = createSharedResource(); + RangerGds.RangerSharedResource resource2 = createSharedResource(); + RangerGds.RangerSharedResource resource3 = createSharedResource(); + List resources = Arrays.asList(resource1, resource2, resource3); + + when(gdsStore.updateSharedResource(resource1)).thenReturn(resource1); + when(gdsStore.updateSharedResource(resource2)).thenReturn(resource2); + when(gdsStore.updateSharedResource(resource3)).thenReturn(resource3); + + gdsREST.updateSharedResources(false, resources); + + verify(gdsStore).updateSharedResource(resource1); + verify(gdsStore).updateSharedResource(resource2); + verify(gdsStore).updateSharedResource(resource3); + verify(gdsStore, Mockito.never()).removeSharedResources(Mockito.anyList()); + } + + @Test + public void testUpdateSharedResources_deletesWhenForceDelete() throws Exception { + RangerGds.RangerSharedResource resource1 = createSharedResource(); + RangerGds.RangerSharedResource resource2 = createSharedResource(); + RangerGds.RangerSharedResource resource3 = createSharedResource(); + List resources = Arrays.asList(resource1, resource2, resource3); + List resourceIds = Arrays.asList(resource1.getId(), resource2.getId(), resource3.getId()); + + doNothing().when(gdsStore).removeSharedResources(resourceIds); + + gdsREST.updateSharedResources(true, resources); + + verify(gdsStore).removeSharedResources(resourceIds); + verify(gdsStore, Mockito.never()).updateSharedResource(Mockito.any(RangerGds.RangerSharedResource.class)); + } + + @Test + public void testUpdateSharedResourcesBatchSizeExceeded() { + List resources = new ArrayList<>(); + for (long i = 0; i < 101; i++) { + resources.add(createSharedResource()); + } + Mockito.when(restErrorUtil.createRESTException(Mockito.anyString())).thenReturn(new WebApplicationException()); + + assertThrows(WebApplicationException.class, () -> gdsREST.updateSharedResources(false, resources)); + } + + @Test + public void testUpdateSharedResourcesNullResourceIds() { + Mockito.when(restErrorUtil.createRESTException(Mockito.anyString())).thenReturn(new WebApplicationException()); + + assertThrows(WebApplicationException.class, () -> gdsREST.updateSharedResources(false, null)); + } + @Test public void testRemoveSharedResourceException() { Mockito.doThrow(new RuntimeException("err")).when(gdsStore).removeSharedResources(any(List.class));