This is an automated email from the ASF dual-hosted git repository.
rmani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 8c543062f5 RANGER-5129: Enhance Dataset Filtering by Validity Schedule
and Expiration Date Range
8c543062f5 is described below
commit 8c543062f50d11c453a8dccfb177a98b4f0d42ff
Author: Radhika Kundam <[email protected]>
AuthorDate: Mon Feb 10 14:03:55 2025 -0800
RANGER-5129: Enhance Dataset Filtering by Validity Schedule and Expiration
Date Range
Signed-off-by: Ramesh Mani <[email protected]>
---
.../RangerValidityScheduleEvaluator.java | 2 +-
.../apache/ranger/plugin/util/SearchFilter.java | 4 +
.../java/org/apache/ranger/biz/GdsDBStore.java | 70 ++++++++++
.../org/apache/ranger/common/RangerSearchUtil.java | 3 +
.../java/org/apache/ranger/rest/TestGdsREST.java | 149 ++++++++++++++++++++-
5 files changed, 226 insertions(+), 2 deletions(-)
diff --git
a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java
b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java
index 027c2140c6..5dac4d9f93 100644
---
a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java
+++
b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java
@@ -50,7 +50,7 @@ public class RangerValidityScheduleEvaluator {
private static final TimeZone defaultTZ = TimeZone.getDefault();
- private static final ThreadLocal<DateFormat> DATE_FORMATTER =
ThreadLocal.withInitial(() -> new
SimpleDateFormat(RangerValiditySchedule.VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION));
+ public static final ThreadLocal<DateFormat> DATE_FORMATTER =
ThreadLocal.withInitial(() -> new
SimpleDateFormat(RangerValiditySchedule.VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION));
private final Date startTime;
private final Date endTime;
diff --git
a/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
b/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
index a260a87828..5a5556a2f2 100755
---
a/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
+++
b/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
@@ -140,6 +140,10 @@ public class SearchFilter {
public static final String IS_DISTINCT = "isDistinct";
// search, sort
public static final String RETRIEVE_ALL_PAGES =
"retrieveAllPages"; // search
public static final String SHARED_WITH_ME = "sharedWithMe";
// search
+ public static final String VALIDITY_EXPIRY_START =
"validityExpiryStart"; // search
+ public static final String VALIDITY_EXPIRY_END =
"validityExpiryEnd"; // search
+ public static final String VALIDITY_TIME_ZONE = "timeZone";
+ public static final String DEFAULT_TIME_ZONE = "GMT";
private Map<String, String> params;
private Map<String, Object[]> multiValueParams;
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java
b/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java
index 312a67a28f..b1431cf71d 100755
--- a/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/GdsDBStore.java
@@ -64,6 +64,10 @@
import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
import org.apache.ranger.plugin.model.RangerPolicyDelta;
import org.apache.ranger.plugin.model.RangerPrincipal.PrincipalType;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
+import
org.apache.ranger.plugin.model.validation.RangerValidityScheduleValidator;
+import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
+import
org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator;
import org.apache.ranger.plugin.store.AbstractGdsStore;
import org.apache.ranger.plugin.store.PList;
import org.apache.ranger.plugin.store.ServiceStore;
@@ -91,10 +95,12 @@
import javax.annotation.PostConstruct;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -107,6 +113,7 @@
import java.util.stream.Collectors;
import static
org.apache.ranger.db.XXGlobalStateDao.RANGER_GLOBAL_STATE_NAME_GDS;
+import static
org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator.DATE_FORMATTER;
import static
org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_GDS_NAME;
@Component
@@ -1786,9 +1793,72 @@ private List<RangerDataset>
fetchDatasetsBySearchCriteria(SearchFilter filter) {
}
}
+ filterDatasetsByValidityExpiration(filter, datasets);
+
return datasets;
}
+ public void filterDatasetsByValidityExpiration(SearchFilter filter,
List<RangerDataset> datasets) {
+ LOG.debug("==> filterDatasetsByValidityExpiration({}, {})", filter,
datasets);
+ String validityCheckStart =
filter.getParam(SearchFilter.VALIDITY_EXPIRY_START);
+ String validityCheckEnd =
filter.getParam(SearchFilter.VALIDITY_EXPIRY_END);
+ String validityTimeZone =
filter.getParam(SearchFilter.VALIDITY_TIME_ZONE);
+ RangerValiditySchedule validityCheckFilter = new
RangerValiditySchedule(validityCheckStart, validityCheckEnd, validityTimeZone,
null);
+ List<ValidationFailureDetails> failures = new
ArrayList<>();
+ RangerValiditySchedule validatedValidityCheckFilter =
validateValidityCheckFilter(validityCheckFilter, failures);
+
+ if (validatedValidityCheckFilter != null) {
+ RangerValidityScheduleEvaluator validityScheduleEvaluator = new
RangerValidityScheduleEvaluator(validatedValidityCheckFilter);
+ datasets.removeIf(dataset -> !isDatasetExpiring(dataset,
validityScheduleEvaluator, failures));
+ }
+
+ if (CollectionUtils.isNotEmpty(failures)) {
+ throw restErrorUtil.createRESTException("Error in finding datasets
expiring between '" + validityCheckStart + "' and '" + validityCheckEnd + "': "
+ failures);
+ }
+ LOG.debug("==> filterDatasetsByValidityExpiration({}, {})", filter,
datasets);
+ }
+
+ private boolean isDatasetExpiring(RangerDataset dataset,
RangerValidityScheduleEvaluator validityScheduleEvaluator,
List<ValidationFailureDetails> failures) {
+ if (dataset.getValiditySchedule() == null) {
+ return false;
+ }
+ String datasetValidityScheduleEndTime =
dataset.getValiditySchedule().getEndTime();
+ if (StringUtils.isEmpty(datasetValidityScheduleEndTime)) {
+ return false;
+ }
+
+ try {
+ Date datasetExpiryTime =
DATE_FORMATTER.get().parse(datasetValidityScheduleEndTime);
+ return
validityScheduleEvaluator.isApplicable(datasetExpiryTime.getTime());
+ } catch (ParseException pe) {
+ failures.add(new ValidationFailureDetails(0, "endTime", "", false,
true, false, "Error parsing endTime:" + datasetValidityScheduleEndTime));
+ }
+ return false;
+ }
+
+ private RangerValiditySchedule
validateValidityCheckFilter(RangerValiditySchedule validityCheckFilter,
List<ValidationFailureDetails> failures) {
+ String startTime = validityCheckFilter.getStartTime();
+ String endTime = validityCheckFilter.getEndTime();
+ String timeZone = validityCheckFilter.getTimeZone();
+
+ if (StringUtils.isEmpty(startTime) && StringUtils.isEmpty(endTime)) {
+ return null;
+ }
+
+ if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {
+ failures.add(new ValidationFailureDetails(0, "startTime,endTime",
"", true, true, false, "empty values"));
+ return null;
+ }
+
+ if (StringUtils.isEmpty(timeZone)) {
+ validityCheckFilter.setTimeZone(SearchFilter.DEFAULT_TIME_ZONE);
+ }
+
+ RangerValidityScheduleValidator validator = new
RangerValidityScheduleValidator(validityCheckFilter);
+
+ return validator.validate(failures);
+ }
+
private PList<RangerDataset>
applyPaginataionAndSorting(List<RangerDataset> datasets, SearchFilter filter) {
int maxRows = filter.getMaxRows();
int startIndex = filter.getStartIndex();
diff --git
a/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
b/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
index 6c93d71040..6833048e66 100755
---
a/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
+++
b/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
@@ -150,6 +150,9 @@ public SearchFilter getSearchFilter(@Nonnull
HttpServletRequest request, List<So
ret.setParam(SearchFilter.UPDATE_TIME_END,
request.getParameter(SearchFilter.UPDATE_TIME_END));
ret.setParam(SearchFilter.RESOURCE_CONTAINS,
request.getParameter(SearchFilter.RESOURCE_CONTAINS));
ret.setParam(SearchFilter.SHARED_WITH_ME,
request.getParameter(SearchFilter.SHARED_WITH_ME));
+ ret.setParam(SearchFilter.VALIDITY_EXPIRY_START,
request.getParameter(SearchFilter.VALIDITY_EXPIRY_START));
+ ret.setParam(SearchFilter.VALIDITY_EXPIRY_END,
request.getParameter(SearchFilter.VALIDITY_EXPIRY_END));
+ ret.setParam(SearchFilter.VALIDITY_TIME_ZONE,
request.getParameter(SearchFilter.VALIDITY_TIME_ZONE));
extractCommonCriteriasForFilter(request, ret, sortFields);
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 11df009a46..143cfe9a90 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
@@ -16,11 +16,15 @@
*/
package org.apache.ranger.rest;
+import org.apache.ranger.biz.GdsDBStore;
+import org.apache.ranger.common.RESTErrorUtil;
import org.apache.ranger.common.RangerSearchUtil;
import org.apache.ranger.plugin.model.RangerGds;
import org.apache.ranger.plugin.model.RangerGrant;
import org.apache.ranger.plugin.model.RangerPolicy;
import org.apache.ranger.plugin.model.RangerPrincipal;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
+import org.apache.ranger.plugin.util.SearchFilter;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +35,10 @@
import org.mockito.junit.MockitoJUnitRunner;
import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.WebApplicationException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -42,6 +49,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
@@ -52,7 +60,11 @@ public class TestGdsREST {
@Mock
RangerSearchUtil searchUtil;
@InjectMocks
- private GdsREST gdsREST = new GdsREST();
+ private GdsREST gdsREST;
+ @InjectMocks
+ GdsDBStore gdsDBStore;
+ @Mock
+ RESTErrorUtil restErrorUtil;
@Test
public void testAddDataSetGrants() {
@@ -207,6 +219,116 @@ public void
testGetDataSetGrantsByPrincipalAndAccessType() {
assertTrue("Grants for Principals: " +
Arrays.toString(requestedPrincipals) + " and AccessTypes: " +
Arrays.toString(nonexistentRequestedAccessTypes) + " should be empty",
updatedPolicyItemsByAccessType.isEmpty());
}
+ @Test
+ public void testSearchDataSetsByValidityPeriod() {
+ List<RangerGds.RangerDataset> rangerDatasets = new ArrayList<>();
+
+ RangerGds.RangerDataset rangerDataset1 = createRangerDataSet();
+ updateDatasetValiditySchedule(rangerDataset1, -5, -1);
+
+ RangerGds.RangerDataset rangerDataset2 = createRangerDataSet();
+ updateDatasetValiditySchedule(rangerDataset2, -5, 5);
+
+ RangerGds.RangerDataset rangerDataset3 = createRangerDataSet();
+ updateDatasetValiditySchedule(rangerDataset3, -10, 2);
+
+ RangerGds.RangerDataset rangerDataset4 = createRangerDataSet();
+ updateDatasetValiditySchedule(rangerDataset4, -2, 15);
+
+ RangerGds.RangerDataset rangerDataset5 = createRangerDataSet();
+ updateDatasetValiditySchedule(rangerDataset5, 5, 15);
+
+ RangerGds.RangerDataset rangerDataset6 = createRangerDataSet();
+ updateDatasetValiditySchedule(rangerDataset6, -15, -5);
+
+ rangerDatasets.addAll(Arrays.asList(rangerDataset1, rangerDataset2,
rangerDataset3, rangerDataset4, rangerDataset5, rangerDataset6));
+ List<RangerGds.RangerDataset> actualDatasets = new
ArrayList<>(rangerDatasets);
+
+ SearchFilter filter = new SearchFilter();
+ filter.setParam(SearchFilter.VALIDITY_TIME_ZONE,
SearchFilter.DEFAULT_TIME_ZONE);
+
+ //ValiditySchedule Filter criteria-1
+ String startTime = getFormattedDateString(-10);
+ String endTime = getFormattedDateString(-2);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_START, startTime);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_END, endTime);
+
+ List<RangerGds.RangerDataset> expectedDatasets =
Arrays.asList(rangerDataset6);
+
+ gdsDBStore.filterDatasetsByValidityExpiration(filter, actualDatasets);
+
+ assertEquals("Datasets expiry count mismatch between " + startTime + "
and " + endTime, expectedDatasets.size(), actualDatasets.size());
+
+ assertTrue("Mismatch in datasets returned for expiry between " +
startTime + " and " + endTime,
+ actualDatasets.containsAll(expectedDatasets));
+
+ //ValiditySchedule Filter criteria-2
+ actualDatasets.clear();
+ actualDatasets.addAll(rangerDatasets);
+
+ startTime = getFormattedDateString(-4);
+ endTime = getFormattedDateString(20);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_START, startTime);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_END, endTime);
+
+ expectedDatasets = Arrays.asList(rangerDataset1, rangerDataset2,
rangerDataset3, rangerDataset4, rangerDataset5);
+
+ gdsDBStore.filterDatasetsByValidityExpiration(filter, actualDatasets);
+
+ assertEquals("Datasets expiry count mismatch between " + startTime + "
and " + endTime, expectedDatasets.size(), actualDatasets.size());
+
+ assertTrue("Mismatch in datasets returned for expiry between " +
startTime + " and " + endTime,
+ actualDatasets.containsAll(expectedDatasets));
+
+ //ValiditySchedule Filter criteria-3
+ actualDatasets.clear();
+ actualDatasets.addAll(rangerDatasets);
+
+ startTime = getFormattedDateString(-15);
+ endTime = getFormattedDateString(0);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_START, startTime);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_END, endTime);
+
+ expectedDatasets = Arrays.asList(rangerDataset1, rangerDataset6);
+
+ gdsDBStore.filterDatasetsByValidityExpiration(filter, actualDatasets);
+
+ assertEquals("Datasets expiry count mismatch between " + startTime + "
and " + endTime, expectedDatasets.size(), actualDatasets.size());
+
+ assertTrue("Mismatch in datasets returned for expiry between " +
startTime + " and " + endTime,
+ actualDatasets.containsAll(expectedDatasets));
+
+ //ValiditySchedule Filter criteria-4 with invalid date
+ actualDatasets.clear();
+ actualDatasets.addAll(rangerDatasets);
+
+ startTime = getInvalidDateString(-5);
+ endTime = getFormattedDateString(16);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_START, startTime);
+ filter.setParam(SearchFilter.VALIDITY_EXPIRY_END, endTime);
+
+
Mockito.when(restErrorUtil.createRESTException(Mockito.anyString())).thenThrow(new
WebApplicationException());
+
+ assertThrows(WebApplicationException.class, () -> {
+ gdsDBStore.filterDatasetsByValidityExpiration(filter,
actualDatasets);
+ });
+
+ //ValiditySchedule Filter criteria-5 without start and end time
+ actualDatasets.clear();
+ actualDatasets.addAll(rangerDatasets);
+
+ filter.removeParam(SearchFilter.VALIDITY_EXPIRY_START);
+ filter.removeParam(SearchFilter.VALIDITY_EXPIRY_END);
+
+ expectedDatasets = rangerDatasets;
+
+ gdsDBStore.filterDatasetsByValidityExpiration(filter, actualDatasets);
+
+ assertEquals("Datasets expiry count mismatch with empty start and end
time", expectedDatasets.size(), actualDatasets.size());
+
+ assertTrue("Mismatch in datasets returned with empty expiry time
range", actualDatasets.containsAll(expectedDatasets));
+ }
+
private RangerGds.RangerDataset createRangerDataSet() {
long id = new Random().nextInt(100);
RangerGds.RangerDataset dataset = new RangerGds.RangerDataset();
@@ -217,6 +339,31 @@ private RangerGds.RangerDataset createRangerDataSet() {
return dataset;
}
+ private RangerGds.RangerDataset
updateDatasetValiditySchedule(RangerGds.RangerDataset dataset, int
pastDaysToStart, int futureDaysToEnd) {
+ String start = getFormattedDateString(pastDaysToStart);
+ String end = getFormattedDateString(futureDaysToEnd);
+ String timezone = SearchFilter.DEFAULT_TIME_ZONE;
+ RangerValiditySchedule validitySchedule = new
RangerValiditySchedule(start, end, timezone, null);
+ dataset.setValiditySchedule(validitySchedule);
+
+ return dataset;
+ }
+
+ private String getFormattedDateString(int days) {
+ return getDateString(days,
RangerValiditySchedule.VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION);
+ }
+
+ private String getInvalidDateString(int days) {
+ return getDateString(days, "yyyy/MM/dd");
+ }
+
+ private String getDateString(int days, String pattern) {
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern);
+ LocalDateTime current = LocalDateTime.now();
+
+ return current.plusDays(days).format(dateFormatter);
+ }
+
private RangerPolicy createPolicyForDataSet(RangerGds.RangerDataset
dataset) {
RangerPolicy policy = new RangerPolicy();
policy.setName("DATASET: " + dataset.getName() + "@" +
System.currentTimeMillis());