diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerAbstractConditionEvaluator.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerAbstractConditionEvaluator.java new file mode 100644 index 0000000000..2f743bcea8 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerAbstractConditionEvaluator.java @@ -0,0 +1,54 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.mockito.Mockito.mock; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerAbstractConditionEvaluator { + @Test + public void test01_settersAndAlwaysMatchPaths() { + RangerSimpleMatcher matcher = new RangerSimpleMatcher(); + + RangerServiceDef serviceDef = mock(RangerServiceDef.class); + matcher.setServiceDef(serviceDef); + + RangerPolicyItemCondition condition = new RangerPolicyItemCondition("__simple", null); + matcher.setPolicyItemCondition(condition); + matcher.setConditionDef(null); + matcher.init(); + + Assertions.assertEquals(condition, matcher.getPolicyItemCondition()); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerAccessedFromClusterConditions.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerAccessedFromClusterConditions.java new file mode 100644 index 0000000000..df94276104 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerAccessedFromClusterConditions.java @@ -0,0 +1,129 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.Collections; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerAccessedFromClusterConditions { + @Test + public void test01_fromCluster_withEmptyList_isAlwaysTrue() { + RangerAccessedFromClusterCondition evaluator = new RangerAccessedFromClusterCondition(); + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Collections.emptyList()); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + RangerAccessRequest req = mock(RangerAccessRequest.class); + Assertions.assertTrue(evaluator.isMatched(req)); + + Assertions.assertTrue(evaluator.isMatched(req)); + } + + @Test + public void test02_fromCluster_withValues_matchesOnlyIfContained() { + RangerAccessedFromClusterCondition evaluator = new RangerAccessedFromClusterCondition(); + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("c1", "c2")); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + RangerAccessRequest req = mock(RangerAccessRequest.class); + when(req.getClusterName()).thenReturn("c2"); + Assertions.assertTrue(evaluator.isMatched(req)); + when(req.getClusterName()).thenReturn("c3"); + Assertions.assertFalse(evaluator.isMatched(req)); + } + + @Test + public void test03_notFromCluster_withEmptyList_alwaysTrue() { + RangerAccessedNotFromClusterCondition evaluator = new RangerAccessedNotFromClusterCondition(); + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Collections.emptyList()); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + RangerAccessRequest req = mock(RangerAccessRequest.class); + Assertions.assertTrue(evaluator.isMatched(req)); + } + + @Test + public void test04_notFromCluster_withValues_trueWhenNotContained() { + RangerAccessedNotFromClusterCondition evaluator = new RangerAccessedNotFromClusterCondition(); + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("c1", "c2")); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + RangerAccessRequest req = mock(RangerAccessRequest.class); + when(req.getClusterName()).thenReturn("c3"); + Assertions.assertTrue(evaluator.isMatched(req)); + when(req.getClusterName()).thenReturn("c1"); + Assertions.assertFalse(evaluator.isMatched(req)); + } + + @Test + public void test05_fromClusterType_withValues() { + RangerAccessedFromClusterTypeCondition evaluator = new RangerAccessedFromClusterTypeCondition(); + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("onprem", "cloud")); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + RangerAccessRequest req = mock(RangerAccessRequest.class); + when(req.getClusterType()).thenReturn("cloud"); + Assertions.assertTrue(evaluator.isMatched(req)); + when(req.getClusterType()).thenReturn("edge"); + Assertions.assertFalse(evaluator.isMatched(req)); + } + + @Test + public void test06_notFromClusterType_withValues() { + RangerAccessedNotFromClusterTypeCondition evaluator = new RangerAccessedNotFromClusterTypeCondition(); + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("onprem", "cloud")); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + RangerAccessRequest req = mock(RangerAccessRequest.class); + when(req.getClusterType()).thenReturn("edge"); + Assertions.assertTrue(evaluator.isMatched(req)); + when(req.getClusterType()).thenReturn("cloud"); + Assertions.assertFalse(evaluator.isMatched(req)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerContextAttributeValueConditions.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerContextAttributeValueConditions.java new file mode 100644 index 0000000000..2c35649a5f --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerContextAttributeValueConditions.java @@ -0,0 +1,174 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerContextAttributeValueConditions { + @Test + public void test01_inCondition_attributeMissing_returnsTrue() { + RangerContextAttributeValueInCondition evaluator = new RangerContextAttributeValueInCondition(); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap("attributeName", "site")); + evaluator.setConditionDef(conditionDef); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("10", "20")); + evaluator.setPolicyItemCondition(condition); + + evaluator.init(); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getContext()).thenReturn(new HashMap<>()); + + Assertions.assertTrue(evaluator.isMatched(request)); + } + + @Test + public void test02_inCondition_attributePresent_withMatch_returnsTrue() { + RangerContextAttributeValueInCondition evaluator = new RangerContextAttributeValueInCondition(); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap("attributeName", "dept")); + evaluator.setConditionDef(conditionDef); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("ENGG", "PROD")); + evaluator.setPolicyItemCondition(condition); + + evaluator.init(); + + Map ctx = new HashMap<>(); + ctx.put("dept", "ENGG"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getContext()).thenReturn(ctx); + + Assertions.assertTrue(evaluator.isMatched(request)); + } + + @Test + public void test03_inCondition_attributePresent_noMatch_returnsFalse() { + RangerContextAttributeValueInCondition evaluator = new RangerContextAttributeValueInCondition(); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap("attributeName", "dept")); + evaluator.setConditionDef(conditionDef); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("ENGG", "PROD")); + evaluator.setPolicyItemCondition(condition); + + evaluator.init(); + + Map ctx = new HashMap<>(); + ctx.put("dept", "SALES"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getContext()).thenReturn(ctx); + + Assertions.assertFalse(evaluator.isMatched(request)); + } + + @Test + public void test04_notInCondition_attributeMissing_returnsTrue() { + RangerContextAttributeValueNotInCondition evaluator = new RangerContextAttributeValueNotInCondition(); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap("attributeName", "site")); + evaluator.setConditionDef(conditionDef); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("10", "20")); + evaluator.setPolicyItemCondition(condition); + + evaluator.init(); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getContext()).thenReturn(new HashMap<>()); + + Assertions.assertTrue(evaluator.isMatched(request)); + } + + @Test + public void test05_notInCondition_attributePresent_withMatch_returnsFalse() { + RangerContextAttributeValueNotInCondition evaluator = new RangerContextAttributeValueNotInCondition(); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap("attributeName", "dept")); + evaluator.setConditionDef(conditionDef); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("ENGG", "PROD")); + evaluator.setPolicyItemCondition(condition); + + evaluator.init(); + + Map ctx = new HashMap<>(); + ctx.put("dept", "PROD"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getContext()).thenReturn(ctx); + + Assertions.assertFalse(evaluator.isMatched(request)); + } + + @Test + public void test06_notInCondition_attributePresent_noMatch_returnsTrue() { + RangerContextAttributeValueNotInCondition evaluator = new RangerContextAttributeValueNotInCondition(); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap("attributeName", "dept")); + evaluator.setConditionDef(conditionDef); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Arrays.asList("ENGG", "PROD")); + evaluator.setPolicyItemCondition(condition); + + evaluator.init(); + + Map ctx = new HashMap<>(); + ctx.put("dept", "SALES"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getContext()).thenReturn(ctx); + + Assertions.assertTrue(evaluator.isMatched(request)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerHiveResourcesAccessConditions.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerHiveResourcesAccessConditions.java new file mode 100644 index 0000000000..145c3fd812 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerHiveResourcesAccessConditions.java @@ -0,0 +1,119 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerRequestedResources; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerHiveResourcesAccessConditions { + @Test + public void test01_accessedTogether_initialized_and_matchesWhenNotMutuallyExcluded() throws Exception { + RangerHiveResourcesAccessedTogetherCondition evaluator = new RangerHiveResourcesAccessedTogetherCondition(); + + RangerServiceDef hiveDef = EmbeddedServiceDefsUtil.instance() + .getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME); + evaluator.setServiceDef(hiveDef); + evaluator.setPolicyItemCondition( + new RangerPolicyItemCondition("together", Arrays.asList("db1.tbl1.col1", "db1.tbl2.col2"))); + evaluator.init(); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + Map ctx = new HashMap<>(); + RangerRequestedResources requested = new RangerRequestedResources(); + + RangerAccessResourceImpl res1 = new RangerAccessResourceImpl(new HashMap<>(), null); + res1.setServiceDef(hiveDef); + res1.setValue("database", "db1"); + res1.setValue("table", "tbl1"); + res1.setValue("column", "col1"); + requested.addRequestedResource(res1); + + RangerAccessResourceImpl res2 = new RangerAccessResourceImpl(new HashMap<>(), null); + res2.setServiceDef(hiveDef); + res2.setValue("database", "db1"); + res2.setValue("table", "tbl2"); + res2.setValue("column", "col2"); + requested.addRequestedResource(res2); + + RangerAccessRequestUtil.setRequestedResourcesInContext(ctx, requested); + when(request.getContext()).thenReturn(ctx); + + Assertions.assertTrue(evaluator.isMatched(request)); + } + + @Test + public void test02_notAccessedTogether_initialized_and_matchesWhenMutuallyExcluded() throws Exception { + RangerHiveResourcesNotAccessedTogetherCondition evaluator = new RangerHiveResourcesNotAccessedTogetherCondition(); + + RangerServiceDef hiveDef = EmbeddedServiceDefsUtil.instance() + .getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME); + evaluator.setServiceDef(hiveDef); + evaluator.setPolicyItemCondition( + new RangerPolicyItemCondition("notTogether", Arrays.asList("db1.tbl1.col1", "db1.tbl2.col2"))); + evaluator.init(); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + Map ctx = new HashMap<>(); + RangerRequestedResources requested = new RangerRequestedResources(); + + RangerAccessResourceImpl res1 = new RangerAccessResourceImpl(new HashMap<>(), null); + res1.setServiceDef(hiveDef); + res1.setValue("database", "db1"); + res1.setValue("table", "tbl1"); + res1.setValue("column", "col1"); + requested.addRequestedResource(res1); + + RangerAccessResourceImpl res3 = new RangerAccessResourceImpl(new HashMap<>(), null); + res3.setServiceDef(hiveDef); + res3.setValue("database", "dbX"); + res3.setValue("table", "tblZ"); + res3.setValue("column", "colZ"); + requested.addRequestedResource(res3); + + RangerAccessRequestUtil.setRequestedResourcesInContext(ctx, requested); + when(request.getContext()).thenReturn(ctx); + + Assertions.assertTrue(evaluator.isMatched(request)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerScriptConditionEvaluator.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerScriptConditionEvaluator.java new file mode 100644 index 0000000000..570fc837e3 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerScriptConditionEvaluator.java @@ -0,0 +1,125 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.util.ScriptEngineUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.SimpleBindings; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerScriptConditionEvaluator { + @Test + public void test01_isMatched_withValidScript_returnsEvaluatorResult() throws Exception { + RangerServiceDef serviceDef = mock(RangerServiceDef.class); + when(serviceDef.getName()).thenReturn("hive"); + + Map evalOptions = new HashMap<>(); + evalOptions.put("engineName", "JavaScript"); + evalOptions.put("enableJsonCtx", "false"); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(evalOptions); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Collections.singletonList("true")); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getReadOnlyCopy()).thenReturn(request); + + ScriptEngine engine = mock(ScriptEngine.class); + + try (MockedStatic ignored = mockStatic(ScriptEngineUtil.class)) { + when(ScriptEngineUtil.createScriptEngine("hive")).thenReturn(engine); + when(engine.createBindings()).thenReturn(new SimpleBindings()); + when(engine.eval(anyString(), any(Bindings.class))).thenReturn(Boolean.TRUE); + + RangerScriptConditionEvaluator evaluator = new RangerScriptConditionEvaluator(); + evaluator.setServiceDef(serviceDef); + evaluator.setConditionDef(conditionDef); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + Assertions.assertTrue(evaluator.isMatched(request)); + } + } + + @Test + public void test02_isMatched_withEmptyScript_logsErrorAndReturnsTrue() { + RangerServiceDef serviceDef = mock(RangerServiceDef.class); + when(serviceDef.getName()).thenReturn("hive"); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.emptyMap()); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Collections.singletonList(" \t ")); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + + try (MockedStatic ignored = mockStatic(ScriptEngineUtil.class)) { + ScriptEngine engine = mock(ScriptEngine.class); + when(ScriptEngineUtil.createScriptEngine("hive")).thenReturn(engine); + + RangerScriptConditionEvaluator evaluator = new RangerScriptConditionEvaluator(); + evaluator.setServiceDef(serviceDef); + evaluator.setConditionDef(conditionDef); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + Assertions.assertTrue(evaluator.isMatched(request)); + } + } + + @Test + public void test03_isMatched_whenEngineNotAvailable_returnsTrue() { + RangerScriptConditionEvaluator evaluator = new RangerScriptConditionEvaluator(); + RangerAccessRequest request = mock(RangerAccessRequest.class); + + Assertions.assertTrue(evaluator.isMatched(request)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerScriptTemplateConditionEvaluator.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerScriptTemplateConditionEvaluator.java new file mode 100644 index 0000000000..3c3856618f --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerScriptTemplateConditionEvaluator.java @@ -0,0 +1,123 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.util.ScriptEngineUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.SimpleBindings; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerScriptTemplateConditionEvaluator { + @Test + public void test01_isMatched_withTemplate_trueExpected_noReverse() throws Exception { + RangerServiceDef serviceDef = mock(RangerServiceDef.class); + when(serviceDef.getName()).thenReturn("hive"); + + Map evalOptions = new HashMap<>(); + evalOptions.put("scriptTemplate", "true"); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(evalOptions); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Collections.singletonList("true")); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getReadOnlyCopy()).thenReturn(request); + + ScriptEngine engine = mock(ScriptEngine.class); + + try (MockedStatic ignored = mockStatic(ScriptEngineUtil.class)) { + when(ScriptEngineUtil.createScriptEngine("hive")).thenReturn(engine); + when(engine.createBindings()).thenReturn(new SimpleBindings()); + when(engine.eval(anyString(), any(Bindings.class))).thenReturn(Boolean.TRUE); + + RangerScriptTemplateConditionEvaluator evaluator = new RangerScriptTemplateConditionEvaluator(); + evaluator.setServiceDef(serviceDef); + evaluator.setConditionDef(conditionDef); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + Assertions.assertTrue(evaluator.isMatched(request)); + } + } + + @Test + public void test02_isMatched_withTemplate_falseExpected_reverseTrue() throws Exception { + RangerServiceDef serviceDef = mock(RangerServiceDef.class); + when(serviceDef.getName()).thenReturn("hive"); + + Map evalOptions = new HashMap<>(); + evalOptions.put("scriptTemplate", "true"); + + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + when(conditionDef.getEvaluatorOptions()).thenReturn(evalOptions); + + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + when(condition.getValues()).thenReturn(Collections.singletonList("false")); + + RangerAccessRequest request = mock(RangerAccessRequest.class); + when(request.getReadOnlyCopy()).thenReturn(request); + + ScriptEngine engine = mock(ScriptEngine.class); + + try (MockedStatic ignored = mockStatic(ScriptEngineUtil.class)) { + when(ScriptEngineUtil.createScriptEngine("hive")).thenReturn(engine); + when(engine.createBindings()).thenReturn(new SimpleBindings()); + when(engine.eval(anyString(), any(Bindings.class))).thenReturn(Boolean.TRUE); + + RangerScriptTemplateConditionEvaluator evaluator = new RangerScriptTemplateConditionEvaluator(); + evaluator.setServiceDef(serviceDef); + evaluator.setConditionDef(conditionDef); + evaluator.setPolicyItemCondition(condition); + evaluator.init(); + + Assertions.assertFalse(evaluator.isMatched(request)); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerTagConditions.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerTagConditions.java new file mode 100644 index 0000000000..91a9477c8f --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerTagConditions.java @@ -0,0 +1,134 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerTag; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.mockito.Mockito.mock; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerTagConditions { + @Test + public void test01_anyOfExpectedTags_present() { + RangerAnyOfExpectedTagsPresentConditionEvaluator evaluator = new RangerAnyOfExpectedTagsPresentConditionEvaluator(); + evaluator.setPolicyItemCondition(new RangerPolicyItemCondition( + "__tagsAny", Arrays.asList("PCI", "PII"))); + evaluator.init(); + + RangerAccessRequest request = buildRequestWithTags(new HashSet<>(Arrays.asList("PCI", "PHI"))); + Assertions.assertTrue(evaluator.isMatched(request)); + } + + @Test + public void test02_anyOfExpectedTags_absent() { + RangerAnyOfExpectedTagsPresentConditionEvaluator evaluator = new RangerAnyOfExpectedTagsPresentConditionEvaluator(); + evaluator.setPolicyItemCondition(new RangerPolicyItemCondition( + "__tagsAny", Arrays.asList("PCI", "PII"))); + evaluator.init(); + + RangerAccessRequest request = buildRequestWithTags(new HashSet<>(Arrays.asList("PHI"))); + Assertions.assertFalse(evaluator.isMatched(request)); + } + + @Test + public void test03_allExpectedTags_present() { + RangerTagsAllPresentConditionEvaluator evaluator = new RangerTagsAllPresentConditionEvaluator(); + evaluator.setPolicyItemCondition(new RangerPolicyItemCondition( + "__tagsAll", Arrays.asList("PCI", "PII"))); + evaluator.init(); + + RangerAccessRequest request = buildRequestWithTags(new HashSet<>(Arrays.asList("PCI", "PII", "PHI"))); + Assertions.assertTrue(evaluator.isMatched(request)); + } + + @Test + public void test04_allExpectedTags_missing() { + RangerTagsAllPresentConditionEvaluator evaluator = new RangerTagsAllPresentConditionEvaluator(); + evaluator.setPolicyItemCondition(new RangerPolicyItemCondition( + "__tagsAll", Arrays.asList("PCI", "PII"))); + evaluator.init(); + + RangerAccessRequest request = buildRequestWithTags(new HashSet<>(Collections.singletonList("PCI"))); + Assertions.assertFalse(evaluator.isMatched(request)); + } + + @Test + public void test05_noneOfExpectedTags_present_returnsFalse() { + RangerNoneOfExpectedTagsPresentConditionEvaluator evaluator = new RangerNoneOfExpectedTagsPresentConditionEvaluator(); + evaluator.setPolicyItemCondition(new RangerPolicyItemCondition( + "__tagsNone", Arrays.asList("PCI", "PII"))); + evaluator.init(); + + RangerAccessRequest request = buildRequestWithTags(new HashSet<>(Collections.singletonList("PII"))); + Assertions.assertFalse(evaluator.isMatched(request)); + } + + @Test + public void test06_noneOfExpectedTags_absent_returnsTrue() { + RangerNoneOfExpectedTagsPresentConditionEvaluator evaluator = new RangerNoneOfExpectedTagsPresentConditionEvaluator(); + evaluator.setPolicyItemCondition(new RangerPolicyItemCondition( + "__tagsNone", Arrays.asList("PCI", "PII"))); + evaluator.init(); + + RangerAccessRequest request = buildRequestWithTags(new HashSet<>(Collections.singletonList("PHI"))); + Assertions.assertTrue(evaluator.isMatched(request)); + } + + private RangerAccessRequest buildRequestWithTags(Set tagTypes) { + RangerAccessResource resource = mock(RangerAccessResource.class); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + request.setResource(resource); + + Set rangerTagForEvals = new HashSet<>(); + for (String type : tagTypes) { + RangerTag tag = new RangerTag(type, Collections.singletonMap("attr1", type + "_v")); + rangerTagForEvals.add(new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF)); + } + + Map ctx = request.getContext(); + RangerAccessRequestUtil.setRequestTagsInContext(ctx, rangerTagForEvals); + return request; + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAbstractContextEnricher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAbstractContextEnricher.java new file mode 100644 index 0000000000..d2f2dfc231 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAbstractContextEnricher.java @@ -0,0 +1,184 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; +import org.apache.ranger.plugin.service.RangerAuthContext; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.net.URL; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerAbstractContextEnricher { + public static class DummyEnricher extends RangerAbstractContextEnricher { + @Override + public void enrich(RangerAccessRequest rangerAccessRequest) { + } + } + + @Test + public void test01_Init_And_PreCleanup_With_AuthContext() { + DummyEnricher enricher = new DummyEnricher(); + + RangerAuthContext authContext = Mockito.mock(RangerAuthContext.class); + doNothing().when(authContext).addOrReplaceRequestContextEnricher(any(RangerContextEnricher.class), any()); + doNothing().when(authContext).cleanupRequestContextEnricher(any(RangerContextEnricher.class)); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, new RangerPolicyEngineOptions()); + RangerPluginContext pluginContext = Mockito.spy(new RangerPluginContext(pluginConfig)); + pluginContext.setAuthContext(authContext); + + enricher.setPluginContext(pluginContext); + enricher.init(); + + boolean ret = enricher.preCleanup(); + assertTrue(ret); + verify(authContext, times(1)).cleanupRequestContextEnricher(enricher); + } + + @Test + public void test02_Init_With_Null_AuthContext() { + DummyEnricher enricher = new DummyEnricher(); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, new RangerPolicyEngineOptions()); + RangerPluginContext pluginContext = new RangerPluginContext(pluginConfig); + // no authContext + enricher.setPluginContext(pluginContext); + + enricher.init(); + boolean ret = enricher.preCleanup(); + assertTrue(ret); + } + + @Test + public void test03_Getters_Setters_And_GetName() { + DummyEnricher enricher = new DummyEnricher(); + RangerContextEnricherDef def = new RangerContextEnricherDef(1L, "MyEnricher", DummyEnricher.class.getName(), new HashMap<>()); + enricher.setEnricherDef(def); + enricher.setServiceName("svc"); + enricher.setServiceDef(new RangerServiceDef()); + enricher.setAppId("appid"); + + assertEquals("MyEnricher", enricher.getName()); + assertEquals("svc", enricher.getServiceName()); + assertEquals("appid", enricher.getAppId()); + assertNotNull(enricher.getServiceDef()); + + RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions(); + opts.optimizeTagTrieForSpace = true; + enricher.setPolicyEngineOptions(opts); + assertEquals(opts, enricher.getPolicyEngineOptions()); + } + + @Test + public void test04_Options_And_Config_Defaults() { + DummyEnricher enricher = new DummyEnricher(); + Map options = new HashMap<>(); + options.put("opt1", "val1"); + RangerContextEnricherDef def = new RangerContextEnricherDef(1L, "E", DummyEnricher.class.getName(), options); + enricher.setEnricherDef(def); + + assertEquals("val1", enricher.getOption("opt1")); + assertEquals("def", enricher.getOption("missing", "def")); + assertTrue(enricher.getBooleanOption("missingBool", true)); + assertFalse(enricher.getBooleanOption("missingBool", false)); + assertEquals('x', enricher.getCharOption("missingChar", 'x')); + assertEquals(42L, enricher.getLongOption("missingLong", 42L)); + + // plugin config absent -> getConfig/int/bool should return defaults + assertEquals("dflt", enricher.getConfig("x", "dflt")); + assertEquals(7, enricher.getIntConfig("x", 7)); + assertTrue(enricher.getBooleanConfig("x", true)); + } + + @Test + public void test05_GetPropertyPrefix_And_PluginConfig() { + DummyEnricher enricher = new DummyEnricher(); + RangerServiceDef svcDef = new RangerServiceDef(); + svcDef.setName("svcType"); + enricher.setServiceDef(svcDef); + + // No plugin context + assertEquals("ranger.plugin.svcType", enricher.getPropertyPrefix()); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, new RangerPolicyEngineOptions()); + RangerPluginContext pluginContext = new RangerPluginContext(pluginConfig); + enricher.setPluginContext(pluginContext); + + assertEquals(pluginConfig, enricher.getPluginConfig()); + assertEquals(pluginContext, enricher.getPluginContext()); + assertEquals(pluginConfig.getPropertyPrefix(), enricher.getPropertyPrefix()); + } + + @Test + public void test06_ReadProperties_From_Resource_And_File() throws Exception { + DummyEnricher enricher = new DummyEnricher(); + + // Resource on classpath (use this test's class as reference): should return null for non-existent + Properties noProps = enricher.readProperties("/non-existent-file.properties"); + assertNull(noProps); + + // Create a temp properties file and read + File tmp = File.createTempFile("ranger-test", ".properties"); + Files.write(tmp.toPath(), Arrays.asList("key=value")); + Properties props = enricher.readProperties(tmp.getAbsolutePath()); + assertNotNull(props); + assertEquals("value", props.getProperty("key")); + + // Also verify resource loading via getResource (write to temp dir and use URL) + URL url = tmp.toURI().toURL(); + assertNotNull(url); + + // cleanup + //noinspection ResultOfMethodCallIgnored + tmp.delete(); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminGdsInfoRetriever.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminGdsInfoRetriever.java new file mode 100644 index 0000000000..0847fe1175 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminGdsInfoRetriever.java @@ -0,0 +1,157 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.util.ServiceGdsInfo; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.nio.channels.ClosedByInterruptException; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerAdminGdsInfoRetriever { + @Test + public void test01_Init_UsesProvidedAdminClient_And_NoDedup() throws Exception { + RangerAdminGdsInfoRetriever retriever = new RangerAdminGdsInfoRetriever(); + RangerServiceDef def = new RangerServiceDef(); + def.setName("svcType"); + retriever.setServiceName("svc"); + retriever.setServiceDef(def); + retriever.setAppId("app"); + + RangerPluginConfig config = new RangerPluginConfig(def.getName(), "svc", "app", null, null, null); + RangerPluginContext pluginCtx = new RangerPluginContext(config); + RangerAdminClient admin = mock(RangerAdminClient.class); + pluginCtx.setAdminClient(admin); + retriever.setPluginContext(pluginCtx); + + Map opts = new HashMap<>(); + opts.put("deDupTags", "false"); + retriever.init(opts); + + ServiceGdsInfo info = mock(ServiceGdsInfo.class); + when(admin.getGdsInfoIfUpdated(anyLong(), anyLong())).thenReturn(info); + + ServiceGdsInfo out = retriever.retrieveGdsInfo(-1L, -1L); + assertNotNull(out); + verify(info, times(0)).dedupStrings(); + } + + @Test + public void test02_Init_CreatesAdminClient_WhenNotProvided_And_DedupTrue() throws Exception { + RangerAdminGdsInfoRetriever retriever = new RangerAdminGdsInfoRetriever(); + RangerServiceDef def = new RangerServiceDef(); + def.setName("svcType"); + retriever.setServiceName("svc"); + retriever.setServiceDef(def); + retriever.setAppId("app"); + + RangerPluginConfig config = new RangerPluginConfig(def.getName(), "svc", "app", null, null, null); + RangerPluginContext pluginCtx = new RangerPluginContext(config); + RangerAdminClient admin = mock(RangerAdminClient.class); + pluginCtx.setAdminClient(admin); + retriever.setPluginContext(pluginCtx); + + Map opts = new HashMap<>(); + opts.put("deDupTags", "true"); + retriever.init(opts); + + ServiceGdsInfo info = mock(ServiceGdsInfo.class); + when(admin.getGdsInfoIfUpdated(anyLong(), anyLong())).thenReturn(info); + + ServiceGdsInfo out = retriever.retrieveGdsInfo(0L, 0L); + assertNotNull(out); + verify(info, times(1)).dedupStrings(); + } + + @Test + public void test03_Init_MissingService_LogsError_NoCrash() { + RangerAdminGdsInfoRetriever retriever = new RangerAdminGdsInfoRetriever(); + assertDoesNotThrow(() -> retriever.init(new HashMap<>())); + } + + @Test + public void test04_Retrieve_ClosedByInterrupt_ThrowsInterrupted() throws Exception { + RangerAdminGdsInfoRetriever retriever = new RangerAdminGdsInfoRetriever(); + RangerServiceDef def = new RangerServiceDef(); + def.setName("svcType"); + retriever.setServiceName("svc"); + retriever.setServiceDef(def); + retriever.setAppId("app"); + + RangerPluginConfig config = new RangerPluginConfig(def.getName(), "svc", "app", null, null, null); + RangerPluginContext pluginCtx = new RangerPluginContext(config); + RangerAdminClient admin = mock(RangerAdminClient.class); + pluginCtx.setAdminClient(admin); + retriever.setPluginContext(pluginCtx); + + retriever.init(new HashMap<>()); + doThrow(new ClosedByInterruptException()).when(admin).getGdsInfoIfUpdated(anyLong(), anyLong()); + + assertThrows(InterruptedException.class, () -> retriever.retrieveGdsInfo(1L, 1L)); + } + + @Test + public void test05_Retrieve_OtherException_ReturnsNull() throws Exception { + RangerAdminGdsInfoRetriever retriever = new RangerAdminGdsInfoRetriever(); + RangerServiceDef def = new RangerServiceDef(); + def.setName("svcType"); + retriever.setServiceName("svc"); + retriever.setServiceDef(def); + retriever.setAppId("app"); + + RangerPluginConfig config = new RangerPluginConfig(def.getName(), "svc", "app", null, null, null); + RangerPluginContext pluginCtx = new RangerPluginContext(config); + RangerAdminClient admin = mock(RangerAdminClient.class); + pluginCtx.setAdminClient(admin); + retriever.setPluginContext(pluginCtx); + + retriever.init(new HashMap<>()); + doThrow(new RuntimeException("boom")).when(admin).getGdsInfoIfUpdated(anyLong(), anyLong()); + + ServiceGdsInfo out = retriever.retrieveGdsInfo(2L, 2L); + assertNull(out); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminTagRetriever.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminTagRetriever.java new file mode 100644 index 0000000000..836d87788d --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminTagRetriever.java @@ -0,0 +1,103 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.util.ServiceTags; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.nio.channels.ClosedByInterruptException; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerAdminTagRetriever { + @Test + public void test01_Init_And_RetrieveTags_Normal_And_Exception() throws Exception { + RangerAdminClient adminClient = Mockito.mock(RangerAdminClient.class); + ServiceTags tags = new ServiceTags(); + when(adminClient.getServiceTagsIfUpdated(anyLong(), anyLong())).thenReturn(tags); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, null); + RangerPluginContext pluginContext = new RangerPluginContext(pluginConfig); + pluginContext.setAdminClient(adminClient); + + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + + RangerAdminTagRetriever retriever = new RangerAdminTagRetriever(); + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + retriever.setPluginConfig(pluginConfig); + retriever.setPluginContext(pluginContext); + retriever.init(new HashMap<>()); + + ServiceTags out = retriever.retrieveTags(-1L, -1L); + assertNotNull(out); + + when(adminClient.getServiceTagsIfUpdated(anyLong(), anyLong())).thenThrow(new RuntimeException("boom")); + out = retriever.retrieveTags(-1L, -1L); + assertNull(out); + } + + @Test + public void test02_RetrieveTags_Interrupted() { + assertThrows(InterruptedException.class, () -> { + RangerAdminClient adminClient = Mockito.mock(RangerAdminClient.class); + when(adminClient.getServiceTagsIfUpdated(anyLong(), anyLong())).thenThrow(new ClosedByInterruptException()); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, null); + RangerPluginContext pluginContext = new RangerPluginContext(pluginConfig); + pluginContext.setAdminClient(adminClient); + + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + + RangerAdminTagRetriever retriever = new RangerAdminTagRetriever(); + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + retriever.setPluginConfig(pluginConfig); + retriever.setPluginContext(pluginContext); + retriever.init(new HashMap<>()); + + retriever.retrieveTags(-1L, -1L); + }); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminUserStoreRetriever.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminUserStoreRetriever.java new file mode 100644 index 0000000000..2fc9dbe6a1 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerAdminUserStoreRetriever.java @@ -0,0 +1,106 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.nio.channels.ClosedByInterruptException; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerAdminUserStoreRetriever { + @Test + public void test01_Init_And_RetrieveUserStore_Normal_And_Exception() throws Exception { + RangerAdminClient adminClient = Mockito.mock(RangerAdminClient.class); + RangerUserStore userStore = new RangerUserStore(); + userStore.setUserStoreVersion(5L); + when(adminClient.getUserStoreIfUpdated(anyLong(), anyLong())).thenReturn(userStore); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, null); + RangerPluginContext pluginContext = new RangerPluginContext(pluginConfig); + pluginContext.setAdminClient(adminClient); + + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + + RangerAdminUserStoreRetriever retriever = new RangerAdminUserStoreRetriever(); + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + retriever.setPluginConfig(pluginConfig); + retriever.setPluginContext(pluginContext); + retriever.init(new HashMap<>()); + + RangerUserStore out = retriever.retrieveUserStoreInfo(-1L, -1L); + assertNotNull(out); + assertEquals(5L, out.getUserStoreVersion()); + + when(adminClient.getUserStoreIfUpdated(anyLong(), anyLong())).thenThrow(new RuntimeException("boom")); + out = retriever.retrieveUserStoreInfo(-1L, -1L); + assertNull(out); + } + + @Test + public void test02_RetrieveUserStore_Interrupted() { + assertThrows(InterruptedException.class, () -> { + RangerAdminClient adminClient = Mockito.mock(RangerAdminClient.class); + when(adminClient.getUserStoreIfUpdated(anyLong(), anyLong())).thenThrow(new ClosedByInterruptException()); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, null); + RangerPluginContext pluginContext = new RangerPluginContext(pluginConfig); + pluginContext.setAdminClient(adminClient); + + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + + RangerAdminUserStoreRetriever retriever = new RangerAdminUserStoreRetriever(); + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + retriever.setPluginConfig(pluginConfig); + retriever.setPluginContext(pluginContext); + retriever.init(new HashMap<>()); + + retriever.retrieveUserStoreInfo(-1L, -1L); + }); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerFileBasedGeolocationProvider.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerFileBasedGeolocationProvider.java new file mode 100644 index 0000000000..c2042adf43 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerFileBasedGeolocationProvider.java @@ -0,0 +1,79 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerFileBasedGeolocationProvider { + @Test + public void test01_Init_And_Enrich_SetsLocationKeys() { + RangerFileBasedGeolocationProvider provider = new RangerFileBasedGeolocationProvider(); + + RangerServiceDef svcDef = new RangerServiceDef(); + svcDef.setName("hive"); + + Map enricherOptions = new HashMap<>(); + enricherOptions.put(RangerAbstractGeolocationProvider.ENRICHER_OPTION_GEOLOCATION_META_PREFIX, ""); + // File-based store reads path using key "FilePath" + enricherOptions.put("FilePath", "/etc/ranger/geo/geo.txt"); + + RangerServiceDef.RangerContextEnricherDef def = new RangerServiceDef.RangerContextEnricherDef(1L, "Geo", provider.getClass().getName(), enricherOptions); + + provider.setServiceDef(svcDef); + provider.setServiceName("svc"); + provider.setAppId("appid"); + provider.setEnricherDef(def); + + RangerPluginConfig pluginConfig = new RangerPluginConfig("hive", "svc", "appid", null, null, null); + RangerPluginContext pluginContext = new RangerPluginContext(pluginConfig); + provider.setPluginContext(pluginContext); + + provider.init(); + + RangerAccessRequestImpl req = new RangerAccessRequestImpl(); + req.setClientIPAddress("20.0.100.85"); // present in default geo data resource range in geo.txt + + provider.enrich(req); + + Map ctx = req.getContext(); + // We expect country and city fields to be populated with LOCATION_ prefix + boolean hasAnyGeo = ctx.keySet().stream().anyMatch(k -> k.startsWith(RangerAbstractGeolocationProvider.KEY_CONTEXT_GEOLOCATION_PREFIX)); + assertTrue(hasAnyGeo); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerFileBasedTagRetriever.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerFileBasedTagRetriever.java new file mode 100644 index 0000000000..470a17c9c1 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerFileBasedTagRetriever.java @@ -0,0 +1,158 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.authorization.utils.JsonUtils; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.util.ServiceTags; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.FileWriter; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerFileBasedTagRetriever { + @Test + public void test01_Init_With_Options_And_Retrieve_From_Resource() throws Exception { + RangerFileBasedTagRetriever retriever = new RangerFileBasedTagRetriever(); + + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + + Map options = new HashMap<>(); + options.put("serviceTagsFileName", "/policyengine/resourceTags.json"); + + retriever.init(options); + + ServiceTags tags = retriever.retrieveTags(-1L, -1L); + assertNotNull(tags); + assertEquals("cl1_hive", tags.getServiceName()); + } + + @Test + public void test02_Init_With_Missing_File_Graceful() { + RangerFileBasedTagRetriever retriever = new RangerFileBasedTagRetriever(); + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + + Map options = new HashMap<>(); + options.put("serviceTagsFileName", "/does/not/exist.json"); + + retriever.init(options); + + ServiceTags out = retriever.retrieveTags(-1L, -1L); + assertNull(out); + } + + @Test + public void test03_Rotate_Tag_Files_When_Not_Initial() throws Exception { + // prepare temp directory with two tag files suffixed _0.json and _1.json + File dir = new File(System.getProperty("java.io.tmpdir"), "rtags-" + System.nanoTime()); + assertTrue(dir.mkdirs()); + File base = new File(dir, "tags.json"); + String basePath = base.getAbsolutePath(); + + ServiceTags t0 = new ServiceTags(); + t0.setServiceName("svc"); + t0.setTagVersion(100L); + ServiceTags t1 = new ServiceTags(); + t1.setServiceName("svc"); + t1.setTagVersion(101L); + + writeTags(new File(basePath + "_0.json"), t0); + writeTags(new File(basePath + "_1.json"), t1); + + RangerFileBasedTagRetriever retriever = new RangerFileBasedTagRetriever(); + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + + Map options = new HashMap<>(); + options.put("serviceTagsFileName", basePath); + options.put("tagFileCount", "2"); + retriever.init(options); + + // first retrieve uses initial file (basePath), which doesn't exist => null + assertNull(retriever.retrieveTags(-1L, -1L)); + + // simulate parsed tagFilesCount and non-initial state + retriever.tagFilesCount = 2; + retriever.currentTagFileIndex = 0; + retriever.isInitial = false; + + // subsequent retrieves rotate between _0 and _1 + ServiceTags r0 = retriever.retrieveTags(-1L, -1L); + ServiceTags r1 = retriever.retrieveTags(-1L, -1L); + assertNotNull(r0); + assertNotNull(r1); + assertTrue(r0.getTagVersion() == 100L || r0.getTagVersion() == 101L); + assertTrue(r1.getTagVersion() == 100L || r1.getTagVersion() == 101L); + } + + @Test + public void test04_GetTagFileURL_OpenStreamError_Graceful() { + RangerFileBasedTagRetriever retriever = new RangerFileBasedTagRetriever(); + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + retriever.setServiceName("svc"); + retriever.setServiceDef(serviceDef); + retriever.setAppId("appid"); + + Map options = new HashMap<>(); + // Provide a bogus path to force URL lookup to fail and log, returning null + options.put("serviceTagsFileName", "/this/path/does/not/exist/tags.json"); + retriever.init(options); + + ServiceTags out = retriever.retrieveTags(-1L, -1L); + assertNull(out); + } + + private static void writeTags(File file, ServiceTags tags) throws Exception { + try (FileWriter fw = new FileWriter(file)) { + fw.write(JsonUtils.objectToJson(tags)); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerGdsEnricher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerGdsEnricher.java new file mode 100644 index 0000000000..843918767d --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerGdsEnricher.java @@ -0,0 +1,88 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; +import org.apache.ranger.plugin.policyengine.gds.GdsPolicyEngine; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerGdsEnricher { + @Test + public void test01_Enrich_With_DataStore_And_Null() { + RangerGdsEnricher enricher = new RangerGdsEnricher(); + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + enricher.setServiceDef(serviceDef); + + GdsPolicyEngine engine = Mockito.mock(GdsPolicyEngine.class); + GdsAccessResult result = new GdsAccessResult(); + when(engine.evaluate(any())).thenReturn(result); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + + // With dataStore engine + enricher.enrich(request, engine); + assertEquals(result, RangerAccessRequestUtil.getGdsResultFromContext(request.getContext())); + + // Without dataStore, and internal engine null -> no NPE and null in context + RangerAccessRequestImpl request2 = new RangerAccessRequestImpl(); + enricher.enrich(request2, null); + assertNull(RangerAccessRequestUtil.getGdsResultFromContext(request2.getContext())); + } + + @Test + public void test02_SetGdsInfo_SetsEngine_And_PreCleanup() { + RangerGdsEnricher enricher = new RangerGdsEnricher(); + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + enricher.setServiceDef(serviceDef); + + // set plugin context and init so serviceDefHelper is initialized and pluginContext is non-null + RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "appid", null, null, null); + RangerPluginContext ctx = new RangerPluginContext(cfg); + enricher.setPluginContext(ctx); + enricher.init(); + + // No need to construct a real GdsPolicyEngine here; verify preCleanup path executes safely + assertTrue(enricher.preCleanup()); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerServiceResourceMatcher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerServiceResourceMatcher.java new file mode 100644 index 0000000000..818a9f9113 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerServiceResourceMatcher.java @@ -0,0 +1,89 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.model.RangerServiceResource; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerServiceResourceMatcher { + @Test + public void test01_Id_IsLeaf_And_GetMatchType() { + RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("hive"); + RangerResourceDef db = new RangerResourceDef(); + db.setItemId(1L); + db.setName("database"); + db.setType("string"); + db.setLevel(0); + db.setIsValidLeaf(true); + serviceDef.setResources(Collections.singletonList(db)); + + RangerServiceResource sr = new RangerServiceResource(); + sr.setId(100L); + sr.setServiceName("hive"); + Map res = new HashMap<>(); + res.put("database", new RangerPolicy.RangerPolicyResource(Collections.singletonList("db1"), false, false)); + sr.setResourceElements(res); + + RangerDefaultPolicyResourceMatcher matcher = new RangerDefaultPolicyResourceMatcher(); + matcher.setServiceDef(serviceDef); + matcher.setPolicyResources(res, RangerPolicy.POLICY_TYPE_ACCESS); + matcher.init(); + + RangerServiceResourceMatcher srm = new RangerServiceResourceMatcher(sr, matcher); + + assertEquals(100L, srm.getId()); + assertTrue(srm.isLeaf("database")); + + RangerAccessResourceImpl reqRes = new RangerAccessResourceImpl(); + reqRes.setServiceDef(serviceDef); + reqRes.setValue("database", Collections.singletonList("db1")); + RangerAccessRequestImpl req = new RangerAccessRequestImpl(); + req.setResource(reqRes); + + RangerPolicyResourceMatcher.MatchType mt = srm.getMatchType(reqRes, req.getResourceElementMatchingScopes(), req.getContext()); + assertNotEquals(RangerPolicyResourceMatcher.MatchType.NONE, mt); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerTagEnricher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerTagEnricher.java new file mode 100644 index 0000000000..bd1804dbf6 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerTagEnricher.java @@ -0,0 +1,741 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import org.apache.ranger.plugin.contextenricher.TestRangerTagEnricher.TagEnricherTestCase.TestData; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.model.RangerServiceResource; +import org.apache.ranger.plugin.model.RangerTag; +import org.apache.ranger.plugin.model.RangerTagDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerMutableResource; +import org.apache.ranger.plugin.policyengine.RangerResourceTrie; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; +import org.apache.ranger.plugin.util.DownloadTrigger; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerServiceTagsDeltaUtil; +import org.apache.ranger.plugin.util.ServiceTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerTagEnricher { + static Gson gsonBuilder; + + @BeforeAll + public static void setUpBeforeClass() throws Exception { + gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z") + .setPrettyPrinting() + .registerTypeAdapter(RangerAccessResource.class, new RangerResourceDeserializer()) + .create(); + } + + @AfterAll + public static void tearDownAfterClass() throws Exception { + } + + @Test + public void test01_RangerTagsForEvalSort() { + List matchTypes = new ArrayList<>(); + + matchTypes.add(null); + matchTypes.add(MatchType.NONE); + matchTypes.add(MatchType.DESCENDANT); + matchTypes.add(MatchType.ANCESTOR); + matchTypes.add(MatchType.SELF_AND_ALL_DESCENDANTS); + matchTypes.add(MatchType.SELF); + + matchTypes.sort(RangerPolicyResourceMatcher.MATCH_TYPE_COMPARATOR); + + assertEquals(matchTypes.get(0), MatchType.SELF); + assertEquals(matchTypes.get(1), MatchType.SELF_AND_ALL_DESCENDANTS); + assertEquals(matchTypes.get(2), MatchType.ANCESTOR); + assertEquals(matchTypes.get(3), MatchType.DESCENDANT); + assertEquals(matchTypes.get(4), MatchType.NONE); + assertNull(matchTypes.get(5)); + } + + @Test + public void test02_TagEnricher_hive() { + String[] hiveTestResourceFiles = {"/contextenricher/test_tagenricher_hive.json"}; + + runTestsFromResourceFiles(hiveTestResourceFiles); + } + + private void runTestsFromResourceFiles(String[] resourceNames) { + for (String resourceName : resourceNames) { + InputStream inStream = this.getClass().getResourceAsStream(resourceName); + InputStreamReader reader = new InputStreamReader(inStream); + + runTests(reader, resourceName); + } + } + + private void runTests(InputStreamReader reader, String testName) { + TagEnricherTestCase testCase = gsonBuilder.fromJson(reader, TagEnricherTestCase.class); + + assertTrue(testCase != null && testCase.serviceDef != null && testCase.serviceResources != null && testCase.tests != null, testName); + + ServiceTags serviceTags = new ServiceTags(); + serviceTags.setServiceName(testCase.serviceName); + serviceTags.setTagDefinitions(testCase.tagDefinitions); + serviceTags.setTags(testCase.tags); + serviceTags.setServiceResources(testCase.serviceResources); + serviceTags.setResourceToTagIds(testCase.resourceToTagIds); + + RangerTagEnricher tagEnricher = new RangerTagEnricher(); + + tagEnricher.setServiceName(testCase.serviceName); + tagEnricher.setServiceDef(testCase.serviceDef); + tagEnricher.init(); + tagEnricher.setServiceTags(serviceTags); + + List expectedTags = new ArrayList<>(); + List resultTags = new ArrayList<>(); + + for (TestData test : testCase.tests) { + RangerAccessRequestImpl request = new RangerAccessRequestImpl(test.resource, test.accessType, "testUser", null, null); + + ((RangerMutableResource) request.getResource()).setServiceDef(testCase.serviceDef); + tagEnricher.enrich(request); + + List expected = test.result; + + Set result = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); + + expectedTags.clear(); + if (expected != null) { + for (RangerTag tag : expected) { + expectedTags.add(tag.getType()); + } + Collections.sort(expectedTags); + } + + resultTags.clear(); + if (result != null) { + for (RangerTagForEval tag : result) { + resultTags.add(tag.getType()); + } + Collections.sort(resultTags); + } + + assertEquals(expectedTags, resultTags, test.name); + } + } + + @Test + public void test03_Init_WithMissingRetrieverName_LogsError_NoCrash() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = new RangerServiceDef(); + def.setName("svcType"); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.setAppId("app"); + + RangerContextEnricherDef ed = new RangerContextEnricherDef(); + ed.setName("tags"); + ed.setEnricherOptions(Collections.emptyMap()); + enricher.setEnricherDef(ed); + + enricher.init(); + assertTrue(true); + } + + @Test + public void test04_SetServiceTags_Null_ClearsAndKeepsVersionMinusOne() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + assertEquals(-1L, enricher.getServiceTagsVersion()); + enricher.setServiceTags(null); + assertEquals(-1L, enricher.getServiceTagsVersion()); + } + + @Test + public void test05_SetServiceTags_NoResources_ReturnsNullEnriched() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags tags = new ServiceTags(); + tags.setServiceName("svc"); + tags.setIsDelta(false); + tags.setTagVersion(1L); + + enricher.setServiceTags(tags); + assertEquals(-1L, enricher.getServiceTagsVersion()); + } + + @Test + public void test06_SetServiceTags_DisableTrieLookupPrefilter_BuildsMatchersOnly() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(tags); + assertEquals(1L, enricher.getServiceTagsVersion()); + assertNotNull(enricher.getEnrichedServiceTags()); + } + + @Test + public void test07_SetServiceTags_Delta_NoChange_UsesExisting() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags base = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(base); + assertEquals(1L, enricher.getServiceTagsVersion()); + + ServiceTags delta = new ServiceTags(); + delta.setServiceName("svc"); + delta.setIsDelta(true); + delta.setTagsChangeExtent(ServiceTags.TagsChangeExtent.NONE); + delta.setTagVersion(2L); + + enricher.setServiceTags(delta); + // For NONE extent, implementation retains existing enriched tags/version + assertEquals(1L, enricher.getServiceTagsVersion()); + } + + @Test + public void test08_PreCleanup_CancelsTimer_And_StopsRefresher() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + assertTrue(enricher.preCleanup()); + } + + @Test + public void test09_Enrich_With_Incorrect_DataStore_Fallbacks() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(tags); + + RangerAccessResourceImpl res = new RangerAccessResourceImpl(); + res.setServiceDef(def); + res.setValue("db", Arrays.asList("sales")); + RangerAccessRequestImpl req = new RangerAccessRequestImpl(); + req.setResource(res); + + enricher.enrich(req, new Object()); + assertNotNull(RangerAccessRequestUtil.getRequestTagsFromContext(req.getContext())); + } + + @Test + public void test10_SyncTagsWithAdmin_WaitsUntilSignaled() throws Exception { + RangerTagEnricher enricher = new RangerTagEnricher(); + DownloadTrigger token = new DownloadTrigger(); + + Thread signaller = new Thread(() -> { + try { + Thread.sleep(50L); + } catch (InterruptedException ignored) { + } + token.signalCompletion(); + }); + signaller.start(); + + enricher.syncTagsWithAdmin(token); + signaller.join(2000L); + assertTrue(!signaller.isAlive()); + } + + @Test + public void test11_GetResourceTrieVersion_BeforeAndAfterSet() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + assertEquals(-1L, enricher.getResourceTrieVersion()); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(tags); + assertEquals(1L, enricher.getResourceTrieVersion()); + } + + @Test + public void test12_CopyServiceResourceTrie_ReturnsDeepCopy() throws Exception { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(tags); + assertNotNull(enricher.getEnrichedServiceTags()); + + Method mCopy = RangerTagEnricher.class.getDeclaredMethod("copyServiceResourceTrie"); + mCopy.setAccessible(true); + @SuppressWarnings("unchecked") + Map> copy = (Map>) mCopy.invoke(enricher); + + Map> original = enricher.getEnrichedServiceTags().getServiceResourceTrie(); + assertNotNull(copy); + assertNotSame(original, copy); + for (Map.Entry> e : original.entrySet()) { + assertTrue(copy.containsKey(e.getKey())); + assertNotSame(e.getValue(), copy.get(e.getKey())); + } + } + + @Test + public void test13_RemoveOldServiceResource_RemovesMatcherAndReturnsAccessResource() throws Exception { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(tags); + assertNotNull(enricher.getEnrichedServiceTags()); + + RangerServiceResource serviceResource = enricher.getEnrichedServiceTags().getServiceTags().getServiceResources().get(0); + + Method mCopy = RangerTagEnricher.class.getDeclaredMethod("copyServiceResourceTrie"); + mCopy.setAccessible(true); + @SuppressWarnings("unchecked") + Map> resourceTries = (Map>) mCopy.invoke(enricher); + + List resourceMatchers = new ArrayList<>(enricher.getEnrichedServiceTags().getServiceResourceMatchers()); + int initialSize = resourceMatchers.size(); + + Method mRemove = RangerTagEnricher.class.getDeclaredMethod("removeOldServiceResource", RangerServiceResource.class, List.class, Map.class); + mRemove.setAccessible(true); + Object ret = mRemove.invoke(enricher, serviceResource, resourceMatchers, resourceTries); + + assertNotNull(ret); + assertEquals(initialSize - 1, resourceMatchers.size()); + } + + @Test + public void test14_ProcessServiceTagDeltas_SuccessfulUpdate_AddsMatcherAndUpdatesTrie() throws Exception { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags base = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(base); + + // Build delta that updates the existing resource (same id), with a new signature + RangerServiceResource updated = new RangerServiceResource(); + updated.setId(1L); + Map resElems = new HashMap<>(); + RangerPolicyResource policyRes = new RangerPolicyResource(Arrays.asList("sales"), null, null); + resElems.put("db", policyRes); + updated.setResourceElements(resElems); + updated.setResourceSignature("sig2"); + + ServiceTags deltas = new ServiceTags(); + deltas.setServiceName("svc"); + deltas.setIsDelta(true); + deltas.setTagsChangeExtent(ServiceTags.TagsChangeExtent.SERVICE_RESOURCE); + deltas.setTagVersion(2L); + List changed = new ArrayList<>(); + changed.add(updated); + deltas.setServiceResources(changed); + + ServiceTags allServiceTags = RangerServiceTagsDeltaUtil.applyDelta(new ServiceTags(base), deltas, false); + + Method mCopy = RangerTagEnricher.class.getDeclaredMethod("copyServiceResourceTrie"); + mCopy.setAccessible(true); + @SuppressWarnings("unchecked") + Map> trieMap = (Map>) mCopy.invoke(enricher); + + Set keysToRemoveFromCache = new HashSet<>(); + + Method mProcess = RangerTagEnricher.class.getDeclaredMethod("processServiceTagDeltas", ServiceTags.class, ServiceTags.class, Map.class, Set.class); + mProcess.setAccessible(true); + Object ret = mProcess.invoke(enricher, deltas, allServiceTags, trieMap, keysToRemoveFromCache); + + assertNotNull(ret); + assertTrue(!keysToRemoveFromCache.isEmpty()); + } + + @Test + public void test15_ProcessServiceTagDeltas_ErrorPath_ReturnsOldAndSetsVersionMinusOne() throws Exception { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags base = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(base); + + RangerServiceResource updated = new RangerServiceResource(); + updated.setId(1L); + Map resElems = new HashMap<>(); + RangerPolicyResource policyRes = new RangerPolicyResource(Arrays.asList("sales"), null, null); + resElems.put("db", policyRes); + updated.setResourceElements(resElems); + updated.setResourceSignature("sig2"); + + ServiceTags deltas = new ServiceTags(); + deltas.setServiceName("svc"); + deltas.setIsDelta(true); + deltas.setTagsChangeExtent(ServiceTags.TagsChangeExtent.SERVICE_RESOURCE); + deltas.setTagVersion(2L); + List changed = new ArrayList<>(); + changed.add(updated); + deltas.setServiceResources(changed); + + ServiceTags allServiceTags = RangerServiceTagsDeltaUtil.applyDelta(new ServiceTags(base), deltas, false); + + Map> emptyTrieMap = new HashMap<>(); + Set keysToRemoveFromCache = new HashSet<>(); + + Method mProcess = RangerTagEnricher.class.getDeclaredMethod("processServiceTagDeltas", ServiceTags.class, ServiceTags.class, Map.class, Set.class); + mProcess.setAccessible(true); + Object ret = mProcess.invoke(enricher, deltas, allServiceTags, emptyTrieMap, keysToRemoveFromCache); + + assertSame(enricher.getEnrichedServiceTags(), ret); + assertEquals(-1L, deltas.getTagVersion()); + assertTrue(keysToRemoveFromCache.isEmpty()); + } + + @Test + public void test16_FindMatchingTags_EmptyResourceAndAnyAccess_ReturnsAllTags() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(tags); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + request.setContext(new HashMap<>()); + request.setAccessType(null); // sets ANY + request.setResource(null); // empty resource + + enricher.enrich(request); + + Set result = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals("T1", result.iterator().next().getType()); + } + + @Test + public void test17_DisableTrieLookupPrefilter_SkipsTrieBuild() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + + Map opts = new HashMap<>(); + opts.put("disableTrieLookupPrefilter", "true"); + RangerContextEnricherDef ed = new RangerContextEnricherDef(); + ed.setName("tags"); + ed.setEnricherOptions(opts); + enricher.setEnricherDef(ed); + + enricher.init(); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(tags); + assertNotNull(enricher.getEnrichedServiceTags()); + assertNull(enricher.getEnrichedServiceTags().getServiceResourceTrie()); + } + + @Test + public void test18_Delta_TagsOnly_PreservesMatchersAndTrie() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags base = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(base); + RangerTagEnricher.EnrichedServiceTags before = enricher.getEnrichedServiceTags(); + assertNotNull(before); + + ServiceTags delta = new ServiceTags(); + delta.setServiceName("svc"); + delta.setIsDelta(true); + delta.setTagsChangeExtent(ServiceTags.TagsChangeExtent.TAGS); + delta.setTagVersion(2L); + + Map tagMap = new HashMap<>(base.getTags()); + RangerTag newTag = new RangerTag(); + newTag.setType("T2"); + tagMap.put(200L, newTag); + delta.setTags(tagMap); + + enricher.setServiceTags(delta); + + RangerTagEnricher.EnrichedServiceTags after = enricher.getEnrichedServiceTags(); + assertSame(before.getServiceResourceMatchers(), after.getServiceResourceMatchers()); + assertSame(before.getServiceResourceTrie(), after.getServiceResourceTrie()); + assertEquals(2L, after.getResourceTrieVersion()); + } + + @Test + public void test19_RemoveOldServiceResource_ErrorWhenTrieMissingKey_ReturnsNull() throws Exception { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags base = makeServiceTagsWithOneResource(def); + enricher.setServiceTags(base); + + RangerServiceResource serviceResource = enricher.getEnrichedServiceTags().getServiceTags().getServiceResources().get(0); + + Method mRemove = RangerTagEnricher.class.getDeclaredMethod("removeOldServiceResource", RangerServiceResource.class, List.class, Map.class); + mRemove.setAccessible(true); + + List resourceMatchers = new ArrayList<>(enricher.getEnrichedServiceTags().getServiceResourceMatchers()); + Map> resourceTries = new HashMap<>(); // missing key forces error path + + Object ret = mRemove.invoke(enricher, serviceResource, resourceMatchers, resourceTries); + assertNull(ret); + } + + @Test + public void test20_Init_WithInvalidRetrieverClass_NotFound() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.setAppId("app"); + + Map opts = new HashMap<>(); + opts.put("tagRetrieverClassName", "com.nonexistent.Foo"); + RangerContextEnricherDef ed = new RangerContextEnricherDef(); + ed.setName("tags"); + ed.setEnricherOptions(opts); + enricher.setEnricherDef(ed); + + enricher.init(); + assertTrue(true); + } + + public static class NotRetriever {} + + @Test + public void test21_Init_WithInvalidRetrieverClass_ClassCast() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.setAppId("app"); + + Map opts = new HashMap<>(); + opts.put("tagRetrieverClassName", NotRetriever.class.getName()); + RangerContextEnricherDef ed = new RangerContextEnricherDef(); + ed.setName("tags"); + ed.setEnricherOptions(opts); + enricher.setEnricherDef(ed); + + enricher.init(); + assertTrue(true); + } + + public abstract static class AbstractRetriever extends RangerTagRetriever {} + + @Test + public void test22_Init_WithAbstractRetriever_InstantiationError() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.setAppId("app"); + + Map opts = new HashMap<>(); + opts.put("tagRetrieverClassName", AbstractRetriever.class.getName()); + RangerContextEnricherDef ed = new RangerContextEnricherDef(); + ed.setName("tags"); + ed.setEnricherOptions(opts); + enricher.setEnricherDef(ed); + + enricher.init(); + assertTrue(true); + } + + @Test + public void test23_GetTagsForServiceResource_NullResourceId_YieldsNoTags() { + RangerTagEnricher enricher = new RangerTagEnricher(); + RangerServiceDef def = minimalSvcDef(); + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.init(); + + ServiceTags tags = makeServiceTagsWithOneResource(def); + // Add another service resource with null id that matches the request; it should not contribute tags + RangerServiceResource rs = new RangerServiceResource(); + rs.setId(null); + Map resElems = new HashMap<>(); + RangerPolicyResource policyRes = new RangerPolicyResource(Arrays.asList("sales"), null, null); + resElems.put("db", policyRes); + rs.setResourceElements(resElems); + rs.setResourceSignature("sig-null"); + tags.getServiceResources().add(rs); + + enricher.setServiceTags(tags); + + RangerAccessResourceImpl res = new RangerAccessResourceImpl(); + res.setServiceDef(def); + res.setValue("db", Arrays.asList("sales")); + RangerAccessRequestImpl req = new RangerAccessRequestImpl(res, "read", "u", null, null); + + enricher.enrich(req); + Set result = RangerAccessRequestUtil.getRequestTagsFromContext(req.getContext()); + assertNotNull(result); + assertEquals(1, result.size()); // only tag from id=1 resource + } + + private static RangerServiceDef minimalSvcDef() { + RangerServiceDef def = new RangerServiceDef(); + def.setName("svcType"); + List resDefs = new ArrayList<>(); + RangerResourceDef r = new RangerResourceDef(); + r.setName("db"); + r.setMatcherOptions(Collections.emptyMap()); + r.setRecursiveSupported(true); + r.setExcludesSupported(true); + resDefs.add(r); + def.setResources(resDefs); + return def; + } + + private static ServiceTags makeServiceTagsWithOneResource(RangerServiceDef def) { + ServiceTags tags = new ServiceTags(); + tags.setServiceName("svc"); + tags.setTagVersion(1L); + tags.setIsDelta(false); + + RangerServiceResource rs = new RangerServiceResource(); + rs.setId(1L); + Map resElems = new HashMap<>(); + RangerPolicyResource policyRes = new RangerPolicyResource(Arrays.asList("sales"), null, null); + resElems.put("db", policyRes); + rs.setResourceElements(resElems); + rs.setResourceSignature("sig"); + + List list = new ArrayList<>(); + list.add(rs); + tags.setServiceResources(list); + Map> resToTags = new HashMap<>(); + resToTags.put(1L, Arrays.asList(100L)); + tags.setResourceToTagIds(resToTags); + Map tagMap = new HashMap<>(); + RangerTag t = new RangerTag(); + t.setType("T1"); + tagMap.put(100L, t); + tags.setTags(tagMap); + return tags; + } + + static class TagEnricherTestCase { + public String serviceName; + public RangerServiceDef serviceDef; + public Map tagDefinitions; + public Map tags; + public List serviceResources; + public Map> resourceToTagIds; + public List tests; + + static class TestData { + public String name; + public RangerAccessResource resource; + public String accessType; + public List result; + } + } + + static class RangerResourceDeserializer implements JsonDeserializer { + @Override + public RangerAccessResource deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + return gsonBuilder.fromJson(jsonObj, RangerAccessResourceImpl.class); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerTagForEval.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerTagForEval.java new file mode 100644 index 0000000000..49872e583a --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerTagForEval.java @@ -0,0 +1,147 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.plugin.model.RangerTag; +import org.apache.ranger.plugin.model.RangerValiditySchedule; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerTagForEval { + @Test + public void test01_IsApplicable_NoValidityPeriods_ReturnsTrue() { + RangerTag tag = new RangerTag(); + tag.setType("TYPE1"); + tag.setAttributes(new HashMap<>()); + tag.setOptions(new HashMap<>()); + tag.setValidityPeriods(new ArrayList<>()); + + RangerTagForEval forEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF); + + assertTrue(forEval.isApplicable(new Date())); + } + + @Test + public void test02_IsApplicable_WithSchedule_CoversTrueAndFalse() { + RangerValiditySchedule schedule = new RangerValiditySchedule(); + SimpleDateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + schedule.setTimeZone(TimeZone.getDefault().getID()); + String nowMinus = fmt.format(new Date(System.currentTimeMillis() - 1000)); + String nowPlus = fmt.format(new Date(System.currentTimeMillis() + 1000)); + schedule.setStartTime(nowMinus); + schedule.setEndTime(nowPlus); + + List validity = new ArrayList<>(); + validity.add(schedule); + + RangerTag tag = new RangerTag(); + tag.setType("TYPE2"); + tag.setValidityPeriods(validity); + tag.setOptions(new HashMap<>()); + + RangerTagForEval forEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.ANCESTOR); + + assertTrue(forEval.isApplicable(new Date())); + + // Outside window: set start in future and end in further future so now is outside + String futureStart = fmt.format(new Date(System.currentTimeMillis() + 600_000)); + String futureEnd = fmt.format(new Date(System.currentTimeMillis() + 1_200_000)); + schedule.setStartTime(futureStart); + schedule.setEndTime(futureEnd); + RangerTagForEval forEval2 = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.ANCESTOR); + assertFalse(forEval2.isApplicable(new Date())); + } + + @Test + public void test03_IsApplicable_WithOptionsLazyInit() { + Map opts = new HashMap<>(); + // Put a validity period JSON string to trigger lazy init path in isApplicable + opts.put(RangerTag.OPTION_TAG_VALIDITY_PERIODS, "[{\"startTime\":\"1\",\"endTime\":\"4102444800000\"}]"); + + RangerTag tag = new RangerTag(); + tag.setType("TYPE3"); + tag.setOptions(opts); + + RangerTagForEval forEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.DESCENDANT); + + assertTrue(forEval.isApplicable(new Date())); + } + + @Test + public void test04_Equals_HashCode_ToString() { + RangerTag tag1 = new RangerTag(); + tag1.setType("T"); + tag1.setAttributes(new HashMap<>()); + tag1.setOptions(new HashMap<>()); + + RangerTag tag2 = new RangerTag(); + tag2.setType("T"); + tag2.setAttributes(new HashMap<>()); + tag2.setOptions(new HashMap<>()); + + RangerTagForEval a = new RangerTagForEval(tag1, RangerPolicyResourceMatcher.MatchType.SELF); + RangerTagForEval b = new RangerTagForEval(tag2, RangerPolicyResourceMatcher.MatchType.SELF); + + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertTrue(a.toString().contains("RangerTagForEval")); + } + + @Test + public void test05_IsApplicable_NullAccessTime_ReturnsTrue() { + RangerTag tag = new RangerTag(); + tag.setType("T"); + RangerTagForEval forEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF); + assertTrue(forEval.isApplicable(null)); + } + + @Test + public void test06_IsApplicable_OptionsNonString_NoEvaluatorCreated_ReturnsTrue() { + Map opts = new HashMap<>(); + opts.put(RangerTag.OPTION_TAG_VALIDITY_PERIODS, 12345L); // non-string triggers empty evaluators + RangerTag tag = new RangerTag(); + tag.setType("T"); + tag.setOptions(opts); + RangerTagForEval forEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF); + assertTrue(forEval.isApplicable(new Date())); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerUserStoreEnricher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerUserStoreEnricher.java new file mode 100644 index 0000000000..1f5d84fea4 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerUserStoreEnricher.java @@ -0,0 +1,157 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerUserStoreEnricher { + @Test + public void test01_Enrich_With_DataStore_And_Fallback() { + RangerUserStoreEnricher enricher = new RangerUserStoreEnricher(); + + RangerUserStore store = new RangerUserStore(); + store.setUserStoreVersion(1L); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + + enricher.enrich(request, store); + assertEquals(store, RangerAccessRequestUtil.getRequestUserStoreFromContext(request.getContext())); + + // with wrong type, falls back to internal field (null) + enricher.enrich(request, new HashMap<>()); + assertNull(RangerAccessRequestUtil.getRequestUserStoreFromContext(request.getContext())); + } + + @Test + public void test02_SetRangerUserStore_Dedup_And_GetVersion() { + RangerUserStoreEnricher enricher = new RangerUserStoreEnricher(); + + RangerUserStore store = new RangerUserStore(); + store.setUserStoreVersion(42L); + + enricher.setRangerUserStore(store); + assertEquals(42L, enricher.getUserStoreVersion()); + assertEquals(store, enricher.getRangerUserStore()); + } + + @Test + public void test03_PreCleanup_Nulls_Timer_And_Refresher() { + RangerUserStoreEnricher enricher = Mockito.spy(new RangerUserStoreEnricher()); + + // Directly invoke preCleanup; no NPE should occur and returns true + assertTrue(enricher.preCleanup()); + } + + @Test + public void test04_Enrich_Without_DataStore_Delegates_To_Internal() { + RangerUserStoreEnricher enricher = new RangerUserStoreEnricher(); + + RangerUserStore store = new RangerUserStore(); + store.setUserStoreVersion(9L); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + + // set internal and call single-arg enrich + enricher.setRangerUserStore(store); + enricher.enrich(request); + assertEquals(store, RangerAccessRequestUtil.getRequestUserStoreFromContext(request.getContext())); + } + + @Test + public void test05_SetRangerUserStore_Null_Clears_Internal() { + RangerUserStoreEnricher enricher = new RangerUserStoreEnricher(); + + RangerUserStore store = new RangerUserStore(); + store.setUserStoreVersion(3L); + enricher.setRangerUserStore(store); + assertNotNull(enricher.getRangerUserStore()); + + enricher.setRangerUserStore(null); + assertNull(enricher.getRangerUserStore()); + assertNull(enricher.getUserStoreVersion()); + } + + @Test + public void test06_Init_With_RetrieverClass_Populates_And_PreCleanup() { + RangerUserStoreEnricher enricher = new RangerUserStoreEnricher(); + + RangerServiceDef def = new RangerServiceDef(); + def.setName("svcType"); + + enricher.setServiceName("svc"); + enricher.setServiceDef(def); + enricher.setAppId("app"); + + Map opts = new HashMap<>(); + opts.put(RangerUserStoreEnricher.USERSTORE_RETRIEVER_CLASSNAME_OPTION, DummyRetriever.class.getName()); + + // attach enricherDef options + RangerContextEnricherDef enricherDef = new RangerContextEnricherDef(); + enricherDef.setName("userstore"); + enricherDef.setEnricherOptions(opts); + enricher.setEnricherDef(enricherDef); + + enricher.init(); + + // Dummy retriever returns a store with version 1L + assertEquals(1L, enricher.getUserStoreVersion()); + assertNotNull(enricher.getRangerUserStore()); + + assertTrue(enricher.preCleanup()); + } + + public static class DummyRetriever extends RangerUserStoreRetriever { + @Override + public void init(Map options) { + // no-op + } + + @Override + public RangerUserStore retrieveUserStoreInfo(long lastKnownVersion, long lastActivationTimeInMillis) { + RangerUserStore store = new RangerUserStore(); + store.setUserStoreVersion(1L); + return store; + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerUserStoreRefresher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerUserStoreRefresher.java new file mode 100644 index 0000000000..34f44121ec --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestRangerUserStoreRefresher.java @@ -0,0 +1,251 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher; + +import com.sun.jersey.api.client.ClientResponse; +import org.apache.ranger.plugin.util.DownloadTrigger; +import org.apache.ranger.plugin.util.JsonUtilsV2; +import org.apache.ranger.plugin.util.RangerRESTClient; +import org.apache.ranger.plugin.util.RangerServiceNotFoundException; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.servlet.http.HttpServletResponse; + +import java.io.File; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerUserStoreRefresher { + @Test + public void test01_populateFromRetriever_savesCache_andUpdatesActivation() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + File cache = File.createTempFile("ruserstore-", ".json"); + cache.deleteOnExit(); + + RangerUserStoreRetriever retriever = mock(RangerUserStoreRetriever.class); + RangerUserStoreEnricher enricher = mock(RangerUserStoreEnricher.class); + + RangerUserStore store = new RangerUserStore(); + store.setUserStoreVersion(7L); + + when(retriever.getServiceName()).thenReturn("svc"); + when(retriever.retrieveUserStoreInfo(anyLong(), anyLong())).thenReturn(store); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retriever, enricher, null, -1L, queue, + cache.getAbsolutePath()); + + RangerUserStore ret = refresher.populateUserStoreInfo(); + + assertNotNull(ret); + verify(enricher, times(1)).setRangerUserStore(store); + assertTrue(cache.exists()); + assertTrue(refresher.getLastActivationTimeInMillis() > 0); + } + + @Test + public void test02_populateFromCache_whenRetrieverReturnsNull() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + File cache = File.createTempFile("ruserstore-", ".json"); + cache.deleteOnExit(); + + RangerUserStoreRetriever retrieverForSeed = mock(RangerUserStoreRetriever.class); + RangerUserStoreEnricher enricherForSeed = mock(RangerUserStoreEnricher.class); + when(retrieverForSeed.getServiceName()).thenReturn("svc"); + + RangerUserStoreRefresher seed = new RangerUserStoreRefresher(retrieverForSeed, enricherForSeed, null, -1L, + queue, cache.getAbsolutePath()); + RangerUserStore seedStore = new RangerUserStore(); + seedStore.setUserStoreVersion(10L); + seed.saveToCache(seedStore); + assertTrue(cache.exists()); + + RangerUserStoreRetriever retriever = mock(RangerUserStoreRetriever.class); + RangerUserStoreEnricher enricher = mock(RangerUserStoreEnricher.class); + when(retriever.getServiceName()).thenReturn("svc"); + when(retriever.retrieveUserStoreInfo(anyLong(), anyLong())).thenReturn(null); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retriever, enricher, null, -1L, queue, + cache.getAbsolutePath()); + + RangerUserStore ret = refresher.populateUserStoreInfo(); + assertNotNull(ret); + assertEquals(10L, ret.getUserStoreVersion()); + verify(enricher, times(1)).setRangerUserStore(ret); + } + + @Test + public void test03_serviceNotFound_disablesCache_andSetsActivation() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + File cache = File.createTempFile("ruserstore-", ".json"); + cache.deleteOnExit(); + + RangerUserStoreRetriever retriever = mock(RangerUserStoreRetriever.class); + RangerUserStoreEnricher enricher = mock(RangerUserStoreEnricher.class); + + when(retriever.getServiceName()).thenReturn("svc"); + when(enricher.isDisableCacheIfServiceNotFound()).thenReturn(true); + when(retriever.retrieveUserStoreInfo(anyLong(), anyLong())) + .thenThrow(new RangerServiceNotFoundException("svc")); + + // seed a cache file to be disabled + RangerUserStoreRefresher seed = new RangerUserStoreRefresher(retriever, enricher, null, -1L, queue, + cache.getAbsolutePath()); + RangerUserStore seedStore = new RangerUserStore(); + seedStore.setUserStoreVersion(5L); + seed.saveToCache(seedStore); + assertTrue(cache.exists()); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retriever, enricher, null, -1L, queue, + cache.getAbsolutePath()); + RangerUserStore ret = refresher.populateUserStoreInfo(); + assertNull(ret); + assertTrue(refresher.getLastActivationTimeInMillis() > 0); + assertTrue(!cache.exists()); + } + + @Test + public void test04_restClient_notModified_returnsNull() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + File cache = File.createTempFile("ruserstore-", ".json"); + cache.deleteOnExit(); + + RangerRESTClient restClient = mock(RangerRESTClient.class); + ClientResponse response = mock(ClientResponse.class); + when(response.getStatus()).thenReturn(HttpServletResponse.SC_NOT_MODIFIED); + when(restClient.get(anyString(), anyMap())).thenReturn(response); + + RangerUserStoreRetriever retrieverForName = mock(RangerUserStoreRetriever.class); + when(retrieverForName.getServiceName()).thenReturn("svc"); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retrieverForName, null, restClient, -1L, queue, + cache.getAbsolutePath()); + RangerUserStore ret = refresher.populateUserStoreInfo(); + assertNull(ret); + } + + @Test + public void test05_restClient_ok_readsAndSavesCache() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + File cache = File.createTempFile("ruserstore-", ".json"); + cache.deleteOnExit(); + + RangerUserStore store = new RangerUserStore(); + store.setUserStoreVersion(77L); + String json = JsonUtilsV2.objToJson(store); + + RangerRESTClient restClient = mock(RangerRESTClient.class); + ClientResponse response = mock(ClientResponse.class); + when(response.getStatus()).thenReturn(HttpServletResponse.SC_OK); + when(response.getEntity(String.class)).thenReturn(json); + when(restClient.get(anyString(), anyMap())).thenReturn(response); + RangerUserStoreRetriever retrieverForName = mock(RangerUserStoreRetriever.class); + when(retrieverForName.getServiceName()).thenReturn("svc"); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retrieverForName, null, restClient, -1L, queue, + cache.getAbsolutePath()); + RangerUserStore ret = refresher.populateUserStoreInfo(); + assertNotNull(ret); + assertEquals(77L, ret.getUserStoreVersion()); + assertTrue(cache.exists()); + assertTrue(refresher.getLastActivationTimeInMillis() > 0); + } + + @Test + public void test06_restClient_notFound_returnsNull() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + RangerRESTClient restClient = mock(RangerRESTClient.class); + ClientResponse response = mock(ClientResponse.class); + when(response.getStatus()).thenReturn(HttpServletResponse.SC_NOT_FOUND); + when(restClient.get(anyString(), anyMap())).thenReturn(response); + RangerUserStoreRetriever retrieverForName = mock(RangerUserStoreRetriever.class); + when(retrieverForName.getServiceName()).thenReturn("svc"); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retrieverForName, null, restClient, -1L, queue, null); + RangerUserStore ret = refresher.populateUserStoreInfo(); + assertNull(ret); + } + + @Test + public void test07_runLoop_processesTrigger_and_stopRefresher() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + RangerUserStoreRetriever retriever = mock(RangerUserStoreRetriever.class); + RangerUserStoreEnricher enricher = mock(RangerUserStoreEnricher.class); + when(retriever.getServiceName()).thenReturn("svc"); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retriever, enricher, null, -1L, queue, null); + refresher.setDaemon(true); + refresher.startRefresher(); + + DownloadTrigger token = new DownloadTrigger(); + queue.put(token); + token.waitForCompletion(); + + refresher.stopRefresher(); + assertTrue(true); + } + + @Test + public void test08_noReceiverNoRestClient_returnsNullAndLogsError() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + RangerUserStoreRetriever retrieverForName = mock(RangerUserStoreRetriever.class); + when(retrieverForName.getServiceName()).thenReturn("svc"); + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retrieverForName, null, null, -1L, queue, null); + RangerUserStore ret = refresher.populateUserStoreInfo(); + assertNull(ret); + } + + @Test + public void test09_restClient_internalServerError_returnsNull() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(); + RangerRESTClient restClient = mock(RangerRESTClient.class); + ClientResponse response = mock(ClientResponse.class); + when(response.getStatus()).thenReturn(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + when(restClient.get(anyString(), anyMap())).thenReturn(response); + RangerUserStoreRetriever retrieverForName = mock(RangerUserStoreRetriever.class); + when(retrieverForName.getServiceName()).thenReturn("svc"); + + RangerUserStoreRefresher refresher = new RangerUserStoreRefresher(retrieverForName, null, restClient, -1L, queue, null); + RangerUserStore ret = refresher.populateUserStoreInfo(); + assertNull(ret); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestTagEnricher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestTagEnricher.java deleted file mode 100644 index 4dc9452965..0000000000 --- a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestTagEnricher.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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.ranger.plugin.contextenricher; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; -import org.apache.ranger.plugin.contextenricher.TestTagEnricher.TagEnricherTestCase.TestData; -import org.apache.ranger.plugin.model.RangerServiceDef; -import org.apache.ranger.plugin.model.RangerServiceResource; -import org.apache.ranger.plugin.model.RangerTag; -import org.apache.ranger.plugin.model.RangerTagDef; -import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; -import org.apache.ranger.plugin.policyengine.RangerAccessResource; -import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; -import org.apache.ranger.plugin.policyengine.RangerMutableResource; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; -import org.apache.ranger.plugin.util.RangerAccessRequestUtil; -import org.apache.ranger.plugin.util.ServiceTags; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -public class TestTagEnricher { - static Gson gsonBuilder; - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z") - .setPrettyPrinting() - .registerTypeAdapter(RangerAccessResource.class, new RangerResourceDeserializer()) - .create(); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - } - - @Test - public void testRangerTagsForEvalSort() { - List matchTypes = new ArrayList<>(); - - matchTypes.add(null); - matchTypes.add(MatchType.NONE); - matchTypes.add(MatchType.DESCENDANT); - matchTypes.add(MatchType.ANCESTOR); - matchTypes.add(MatchType.SELF_AND_ALL_DESCENDANTS); - matchTypes.add(MatchType.SELF); - - matchTypes.sort(RangerPolicyResourceMatcher.MATCH_TYPE_COMPARATOR); - - assertEquals(matchTypes.get(0), MatchType.SELF); - assertEquals(matchTypes.get(1), MatchType.SELF_AND_ALL_DESCENDANTS); - assertEquals(matchTypes.get(2), MatchType.ANCESTOR); - assertEquals(matchTypes.get(3), MatchType.DESCENDANT); - assertEquals(matchTypes.get(4), MatchType.NONE); - assertNull(matchTypes.get(5)); - } - - @Test - public void testTagEnricher_hive() { - String[] hiveTestResourceFiles = {"/contextenricher/test_tagenricher_hive.json"}; - - runTestsFromResourceFiles(hiveTestResourceFiles); - } - - private void runTestsFromResourceFiles(String[] resourceNames) { - for (String resourceName : resourceNames) { - InputStream inStream = this.getClass().getResourceAsStream(resourceName); - InputStreamReader reader = new InputStreamReader(inStream); - - runTests(reader, resourceName); - } - } - - private void runTests(InputStreamReader reader, String testName) { - TagEnricherTestCase testCase = gsonBuilder.fromJson(reader, TagEnricherTestCase.class); - - assertTrue("invalid input: " + testName, testCase != null && testCase.serviceDef != null && testCase.serviceResources != null && testCase.tests != null); - - ServiceTags serviceTags = new ServiceTags(); - serviceTags.setServiceName(testCase.serviceName); - serviceTags.setTagDefinitions(testCase.tagDefinitions); - serviceTags.setTags(testCase.tags); - serviceTags.setServiceResources(testCase.serviceResources); - serviceTags.setResourceToTagIds(testCase.resourceToTagIds); - - RangerTagEnricher tagEnricher = new RangerTagEnricher(); - - tagEnricher.setServiceName(testCase.serviceName); - tagEnricher.setServiceDef(testCase.serviceDef); - tagEnricher.init(); - tagEnricher.setServiceTags(serviceTags); - - List expectedTags = new ArrayList<>(); - List resultTags = new ArrayList<>(); - - for (TestData test : testCase.tests) { - RangerAccessRequestImpl request = new RangerAccessRequestImpl(test.resource, test.accessType, "testUser", null, null); - - ((RangerMutableResource) request.getResource()).setServiceDef(testCase.serviceDef); - tagEnricher.enrich(request); - - List expected = test.result; - - Set result = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); - - expectedTags.clear(); - if (expected != null) { - for (RangerTag tag : expected) { - expectedTags.add(tag.getType()); - } - Collections.sort(expectedTags); - } - - resultTags.clear(); - if (result != null) { - for (RangerTagForEval tag : result) { - resultTags.add(tag.getType()); - } - Collections.sort(resultTags); - } - - assertEquals(test.name, expectedTags, resultTags); - } - } - - static class TagEnricherTestCase { - public String serviceName; - public RangerServiceDef serviceDef; - public Map tagDefinitions; - public Map tags; - public List serviceResources; - public Map> resourceToTagIds; - public List tests; - - static class TestData { - public String name; - public RangerAccessResource resource; - public String accessType; - public List result; - } - } - - static class RangerResourceDeserializer implements JsonDeserializer { - @Override - public RangerAccessResource deserialize(JsonElement jsonObj, Type type, - JsonDeserializationContext context) throws JsonParseException { - return gsonBuilder.fromJson(jsonObj, RangerAccessResourceImpl.class); - } - } -} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestGetFromDataFile.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestGetFromDataFile.java new file mode 100644 index 0000000000..09a8980afd --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestGetFromDataFile.java @@ -0,0 +1,62 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher.externalretrievers; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestGetFromDataFile { + @Test + public void test01_getFromDataFile_readsPropertiesAndMapsToUserStore() throws Exception { + File tmp = File.createTempFile("userattrs", ".properties"); + try (FileOutputStream fos = new FileOutputStream(tmp)) { + String content = "alice=US\n" + "bob=EU\n"; + fos.write(content.getBytes(StandardCharsets.UTF_8)); + } + + GetFromDataFile gf = new GetFromDataFile(); + Map> out = gf.getFromDataFile(tmp.getAbsolutePath(), "region"); + assertEquals("US", out.get("alice").get("region")); + assertEquals("EU", out.get("bob").get("region")); + } + + @Test + public void test02_getFromDataFile_missingFile_returnsEmpty() { + GetFromDataFile gf = new GetFromDataFile(); + Map> out = gf.getFromDataFile("/path/does/not/exist.props", "attr"); + assertTrue(out.isEmpty()); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestGetFromURL.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestGetFromURL.java new file mode 100644 index 0000000000..7a3d39919e --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestGetFromURL.java @@ -0,0 +1,272 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher.externalretrievers; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestGetFromURL { + @Test + public void test01_toUserAttributes_flattensListsIntoCommaSeparated() throws Exception { + GetFromURL gu = new GetFromURL(); + Method m = GetFromURL.class.getDeclaredMethod("toUserAttributes", Map.class); + m.setAccessible(true); + + Map>> input = new HashMap<>(); + Map> attrs = new HashMap<>(); + attrs.put("region", new ArrayList() { + { + add("US"); + add("EU"); + } + }); + input.put("alice", attrs); + + @SuppressWarnings("unchecked") + Map> out = (Map>) m.invoke(gu, input); + assertEquals("US,EU", out.get("alice").get("region")); + } + + @Test + public void test02_verifyToken_missingFields_throwsIOException() throws Exception { + GetFromURL gu = new GetFromURL(); + Method decode = GetFromURL.class.getDeclaredMethod("decodeSecrets", String.class); + decode.setAccessible(true); + Method verify = GetFromURL.class.getDeclaredMethod("verifyToken", String.class); + verify.setAccessible(true); + + String bare = "{\"headers\":[],\"params\":[]}"; // missing tokenUrl + String encoded = (String) decode.invoke(gu, Base64.getEncoder().encodeToString(bare.getBytes())); + assertThrows(IOException.class, () -> { + try { + verify.invoke(gu, encoded); + } catch (Exception e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } + throw new RuntimeException(e); + } + }); + } + + public static class Response { + final int status; + final String body; + + Response(int status, String body) { + this.status = status; + this.body = body == null ? "" : body; + } + } + + public static class MiniHttpServer implements AutoCloseable { + private final Map routes; + private ServerSocket server; + private Thread thread; + private volatile boolean running; + + MiniHttpServer(Map routes) { + this.routes = routes; + } + + int start() throws IOException { + server = new ServerSocket(0); + running = true; + thread = new Thread(() -> { + while (running && !server.isClosed()) { + try (Socket socket = server.accept(); + BufferedReader reader = new BufferedReader( + new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); + PrintWriter writer = new PrintWriter( + new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true)) { + String requestLine = reader.readLine(); + if (requestLine == null) { + continue; + } + String[] parts = requestLine.split(" "); + String path = parts.length >= 2 ? parts[1] : "/"; + // consume headers + String line; + while ((line = reader.readLine()) != null && !line.isEmpty()) { + /* ignore */ } + Response resp = routes.getOrDefault(path, new Response(404, "")); + byte[] bodyBytes = resp.body.getBytes(StandardCharsets.UTF_8); + writer.printf("HTTP/1.1 %d %s\r\n", resp.status, resp.status == 200 ? "OK" : "ERR"); + writer.printf("Content-Length: %d\r\n", bodyBytes.length); + writer.print("Content-Type: application/json\r\n"); + writer.print("Connection: close\r\n\r\n"); + writer.flush(); + socket.getOutputStream().write(bodyBytes); + socket.getOutputStream().flush(); + } catch (IOException e) { + if (running) { + // ignore transient errors while running + } + } + } + }, "mini-http"); + thread.setDaemon(true); + thread.start(); + return server.getLocalPort(); + } + + @Override + public void close() throws IOException { + running = false; + if (server != null && !server.isClosed()) { + server.close(); + } + try { + if (thread != null) { + thread.join(1000); + } + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + } + + @Test + public void test03_getFromURL_withLocalServer_success() throws Exception { + Map routes = new HashMap<>(); + routes.put("/token", new Response(200, "{\"access_token\":\"t123\"}")); + routes.put("/user", new Response(200, "{\"body\":{\"alice\":{\"region\":[\"US\",\"EU\"]}}}")); + try (MiniHttpServer server = new MiniHttpServer(routes)) { + int port = server.start(); + File secrets = File.createTempFile("secrets", ".txt"); + try { + String json = "{\"tokenUrl\":\"http://127.0.0.1:" + port + + "/token\",\"headers\":[{\"Content-Type\":\"application/x-www-form-urlencoded\"}],\"params\":[]}"; + String encoded = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); + Files.write(secrets.toPath(), encoded.getBytes(StandardCharsets.UTF_8)); + + GetFromURL gu = new GetFromURL(); + Map> out = gu.getFromURL("http://127.0.0.1:" + port + "/user", + secrets.getAbsolutePath()); + assertEquals("US,EU", out.get("alice").get("region")); + } finally { + secrets.delete(); + } + } + } + + @Test + public void test04_getFromURL_httpError_throws() throws Exception { + Map routes = new HashMap<>(); + routes.put("/token", new Response(200, "{\"access_token\":\"t123\"}")); + routes.put("/user", new Response(500, "")); + try (MiniHttpServer server = new MiniHttpServer(routes)) { + int port = server.start(); + File secrets = File.createTempFile("secrets", ".txt"); + try { + String json = "{\"tokenUrl\":\"http://127.0.0.1:" + port + + "/token\",\"headers\":[{\"Content-Type\":\"application/x-www-form-urlencoded\"}],\"params\":[]}"; + String encoded = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); + Files.write(secrets.toPath(), encoded.getBytes(StandardCharsets.UTF_8)); + + GetFromURL gu = new GetFromURL(); + assertThrows(IOException.class, + () -> gu.getFromURL("http://127.0.0.1:" + port + "/user", secrets.getAbsolutePath())); + } finally { + secrets.delete(); + } + } + } + + @Test + public void test05_getBearerToken_httpError_throws() throws Exception { + Map routes = new HashMap<>(); + routes.put("/badtoken", new Response(400, "")); + routes.put("/user", new Response(200, "{\"body\":{}}")); + try (MiniHttpServer server = new MiniHttpServer(routes)) { + int port = server.start(); + File secrets = File.createTempFile("secrets", ".txt"); + try { + String json = "{\"tokenUrl\":\"http://127.0.0.1:" + port + + "/badtoken\",\"headers\":[{\"Content-Type\":\"application/x-www-form-urlencoded\"}],\"params\":[]}"; + String encoded = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); + Files.write(secrets.toPath(), encoded.getBytes(StandardCharsets.UTF_8)); + + GetFromURL gu = new GetFromURL(); + assertThrows(IOException.class, + () -> gu.getFromURL("http://127.0.0.1:" + port + "/user", secrets.getAbsolutePath())); + } finally { + secrets.delete(); + } + } + } + + @Test + public void test06_verifyToken_invalidContentType_throws() throws Exception { + GetFromURL gu = new GetFromURL(); + Method verify = GetFromURL.class.getDeclaredMethod("verifyToken", String.class); + verify.setAccessible(true); + String bad = "{\"tokenUrl\":\"http://host/token\",\"headers\":[{\"Content-Type\":\"application/json\"}],\"params\":[]}"; + assertThrows(IOException.class, () -> { + try { + verify.invoke(gu, bad); + } catch (Exception e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } + throw new RuntimeException(e); + } + }); + } + + @Test + public void test07_decodeSecrets_roundTrip() throws Exception { + GetFromURL gu = new GetFromURL(); + Method decode = GetFromURL.class.getDeclaredMethod("decodeSecrets", String.class); + decode.setAccessible(true); + String original = "hello"; + String b64 = Base64.getEncoder().encodeToString(original.getBytes(StandardCharsets.UTF_8)); + String decoded = (String) decode.invoke(gu, b64); + assertEquals(original, decoded); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestRangerMultiSourceUserStoreRetriever.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestRangerMultiSourceUserStoreRetriever.java new file mode 100644 index 0000000000..31890bc8c0 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/externalretrievers/TestRangerMultiSourceUserStoreRetriever.java @@ -0,0 +1,393 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.contextenricher.externalretrievers; + +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerRole; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.util.RangerRoles; +import org.apache.ranger.plugin.util.RangerRolesUtil; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerMultiSourceUserStoreRetriever { + @Test + public void test01_toRetrieverOptions_parsesKeyValuePairs() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method m = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("toRetrieverOptions", Map.class); + m.setAccessible(true); + Map opts = new HashMap<>(); + opts.put("retriever0_api", "attrName=region, userStoreURL=http://host"); + + @SuppressWarnings("unchecked") + Map> parsed = (Map>) m.invoke(r, opts); + assertEquals("region", parsed.get("retriever0_api").get("attrName")); + assertEquals("http://host", parsed.get("retriever0_api").get("userStoreURL")); + } + + @Test + public void test02_retrieveUserAttrFromRoles_filtersByPrefixAndFlattensValues() { + RangerRoles roles = new RangerRoles(); + Set roleSet = new HashSet<>(); + + RangerRole role1 = new RangerRole(); + role1.setName("region.US"); + role1.setUsers(Arrays.asList(new RangerRole.RoleMember("alice", true))); + roleSet.add(role1); + + RangerRole role2 = new RangerRole(); + role2.setName("region.EU"); + role2.setUsers(Arrays.asList(new RangerRole.RoleMember("alice", true))); + roleSet.add(role2); + + RangerRoles rr = new RangerRoles(); + rr.setRangerRoles(roleSet); + + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + RangerPluginConfig pc = Mockito.mock(RangerPluginConfig.class); + RangerPluginContext ctx = new RangerPluginContext(pc); + r.setPluginContext(ctx); + + Map options = new HashMap<>(); + options.put("attrName", "region"); + + try { + Field f = RangerMultiSourceUserStoreRetriever.class.getDeclaredField("rolesUtil"); + f.setAccessible(true); + f.set(r, new RangerRolesUtil(rr)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Map> out = r.retrieveUserAttrFromRoles("retriever0_role", options); + assertEquals("US,EU", out.get("alice").get("region")); + } + + @Test + public void test03_retrieveUserStoreInfo_fetchesRolesWhenRoleRetrieverConfigured() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Map enricherOptions = new HashMap<>(); + enricherOptions.put("retriever0_role", "attrName=region"); + + RangerPluginConfig pc = Mockito.mock(RangerPluginConfig.class); + when(pc.getServiceName()).thenReturn("svc"); + when(pc.getAppId()).thenReturn("app"); + when(pc.getPropertyPrefix()).thenReturn("ranger.plugin.svc"); + when(pc.get("ranger.plugin.svc.policy.rest.url")).thenReturn("http://localhost:6080"); + RangerPluginContext ctx = Mockito.spy(new RangerPluginContext(pc)); + + RangerAdminClient admin = Mockito.mock(RangerAdminClient.class); + Mockito.doReturn(admin).when(ctx).createAdminClient(pc); + when(admin.getRolesIfUpdated(Mockito.anyLong(), Mockito.anyLong())).thenReturn(new RangerRoles()); + + r.setPluginContext(ctx); + r.setPluginConfig(pc); + r.init(enricherOptions); + + RangerUserStore store = r.retrieveUserStoreInfo(-1, System.currentTimeMillis()); + assertNotNull(store); + assertNotNull(store.getUserAttrMapping()); + assertTrue(store.getUserAttrMapping().isEmpty()); + } + + @Test + public void test04_toRetrieverOptions_invalidOptions_parsesEmptyValue() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method m = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("toRetrieverOptions", String.class, + String.class); + m.setAccessible(true); + @SuppressWarnings("unchecked") + Map parsed = (Map) m.invoke(r, "retriever0_api", + "attrName=region, userStoreURL"); + assertEquals("region", parsed.get("attrName")); + assertTrue(parsed.containsKey("userStoreURL")); + assertEquals("", parsed.get("userStoreURL")); + } + + @Test + public void test05_retrieveAll_withUnknownSource_throws() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Map> opts = new HashMap<>(); + opts.put("retriever9_unknown", new HashMap()); + Field f = RangerMultiSourceUserStoreRetriever.class.getDeclaredField("retrieverOptions"); + f.setAccessible(true); + f.set(r, opts); + + Method retrieveAll = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("retrieveAll"); + retrieveAll.setAccessible(true); + assertThrows(Exception.class, () -> { + try { + retrieveAll.invoke(r); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @Test + public void test06_mergeUserAttributes_mergesAndOverwrites() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method merge = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("mergeUserAttributes", Map.class, + Map.class); + merge.setAccessible(true); + Map> dest = new HashMap<>(); + Map alice = new HashMap<>(); + alice.put("region", "US"); + dest.put("alice", alice); + Map> src = new HashMap<>(); + Map alice2 = new HashMap<>(); + alice2.put("dept", "eng"); + alice2.put("region", "EU"); + src.put("alice", alice2); + + merge.invoke(r, src, dest); + assertEquals("EU", dest.get("alice").get("region")); + assertEquals("eng", dest.get("alice").get("dept")); + } + + @Test + public void test07_retrieveUserAttributes_fromDataFile_path() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method m = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("retrieveUserAttributes", String.class, + Map.class); + m.setAccessible(true); + File tmp = File.createTempFile("userattrs", ".properties"); + Files.write(tmp.toPath(), "alice=US\n".getBytes(StandardCharsets.UTF_8)); + Map options = new HashMap<>(); + options.put("attrName", "region"); + options.put("dataFile", tmp.getAbsolutePath()); + @SuppressWarnings("unchecked") + Map> out = (Map>) m.invoke(r, "retriever0_api", + options); + assertEquals("US", out.get("alice").get("region")); + } + + @Test + public void test08_retrieveUserAttributes_missingAttrName_throws() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method m = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("retrieveUserAttributes", String.class, + Map.class); + m.setAccessible(true); + Map options = new HashMap<>(); + options.put("userStoreURL", "http://host"); + assertThrows(Exception.class, () -> { + try { + m.invoke(r, "retriever0_api", options); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @Test + public void test09_retrieveUserAttributes_missingUrlAndDataFile_throws() throws Exception { + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method m = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("retrieveUserAttributes", String.class, + Map.class); + m.setAccessible(true); + Map options = new HashMap<>(); + options.put("attrName", "region"); + assertThrows(Exception.class, () -> { + try { + m.invoke(r, "retriever0_api", options); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + public static class Response { + public final int status; + public final String body; + + public Response(int status, String body) { + this.status = status; + this.body = body == null ? "" : body; + } + } + + public static class MiniHttpServer implements AutoCloseable { + private final Map routes; + private ServerSocket server; + private Thread thread; + private volatile boolean running; + + public MiniHttpServer(Map routes) { + this.routes = routes; + } + + public int start() throws IOException { + server = new ServerSocket(0); + running = true; + thread = new Thread(() -> { + while (running && !server.isClosed()) { + try (Socket socket = server.accept(); + BufferedReader reader = new BufferedReader( + new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); + PrintWriter writer = new PrintWriter( + new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true)) { + String requestLine = reader.readLine(); + if (requestLine == null) { + continue; + } + String[] parts = requestLine.split(" "); + String path = parts.length >= 2 ? parts[1] : "/"; + String line; + while ((line = reader.readLine()) != null && !line.isEmpty()) { + /* ignore */ } + Response resp = routes.getOrDefault(path, new Response(404, "")); + byte[] bodyBytes = resp.body.getBytes(StandardCharsets.UTF_8); + writer.printf("HTTP/1.1 %d %s\r\n", resp.status, resp.status == 200 ? "OK" : "ERR"); + writer.printf("Content-Length: %d\r\n", bodyBytes.length); + writer.print("Content-Type: application/json\r\n"); + writer.print("Connection: close\r\n\r\n"); + writer.flush(); + socket.getOutputStream().write(bodyBytes); + socket.getOutputStream().flush(); + } catch (IOException e) { + if (running) { + // ignore + } + } + } + }, "mini-http"); + thread.setDaemon(true); + thread.start(); + return server.getLocalPort(); + } + + @Override + public void close() throws IOException { + running = false; + if (server != null && !server.isClosed()) { + server.close(); + } + try { + if (thread != null) { + thread.join(1000); + } + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + } + + @Test + public void test10_retrieveUserAttributes_urlSuccess_and_httpError() throws Exception { + // success path + Map routes1 = new HashMap<>(); + routes1.put("/token", new Response(200, "{\"access_token\":\"t123\"}")); + routes1.put("/user", new Response(200, "{\"body\":{\"alice\":{\"region\":[\"US\",\"EU\"]}}}")); + try (MiniHttpServer server = new MiniHttpServer(routes1)) { + int port = server.start(); + File secrets = File.createTempFile("secrets", ".txt"); + try { + String json = "{\"tokenUrl\":\"http://127.0.0.1:" + port + + "/token\",\"headers\":[{\"Content-Type\":\"application/x-www-form-urlencoded\"}],\"params\":[]}"; + String encoded = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); + Files.write(secrets.toPath(), encoded.getBytes(StandardCharsets.UTF_8)); + + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method m = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("retrieveUserAttributes", + String.class, Map.class); + m.setAccessible(true); + Map options = new HashMap<>(); + options.put("attrName", "region"); + options.put("userStoreURL", "http://127.0.0.1:" + port + "/user"); + options.put("configFile", secrets.getAbsolutePath()); + @SuppressWarnings("unchecked") + Map> out = (Map>) m.invoke(r, "retriever0_api", + options); + assertEquals("US,EU", out.get("alice").get("region")); + } finally { + secrets.delete(); + } + } + + // error path: token 400 + Map routes2 = new HashMap<>(); + routes2.put("/badtoken", new Response(400, "")); + routes2.put("/user", new Response(200, "{\"body\":{}}")); + try (MiniHttpServer server = new MiniHttpServer(routes2)) { + int port = server.start(); + File secrets = File.createTempFile("secrets", ".txt"); + try { + String json = "{\"tokenUrl\":\"http://127.0.0.1:" + port + + "/badtoken\",\"headers\":[{\"Content-Type\":\"application/x-www-form-urlencoded\"}],\"params\":[]}"; + String encoded = Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)); + Files.write(secrets.toPath(), encoded.getBytes(StandardCharsets.UTF_8)); + + RangerMultiSourceUserStoreRetriever r = new RangerMultiSourceUserStoreRetriever(); + Method m = RangerMultiSourceUserStoreRetriever.class.getDeclaredMethod("retrieveUserAttributes", + String.class, Map.class); + m.setAccessible(true); + Map options = new HashMap<>(); + options.put("attrName", "region"); + options.put("userStoreURL", "http://127.0.0.1:" + port + "/user"); + options.put("configFile", secrets.getAbsolutePath()); + assertThrows(Exception.class, () -> { + try { + m.invoke(r, "retriever0_api", options); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } finally { + secrets.delete(); + } + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestBinarySearchTree.java b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestBinarySearchTree.java new file mode 100644 index 0000000000..4de916ebcf --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestBinarySearchTree.java @@ -0,0 +1,112 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.geo; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestBinarySearchTree { + @Test + public void test01_insert_and_find() { + BinarySearchTree tree = new BinarySearchTree<>(); + RangerGeolocationData a = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.3", "A"}, 0, true); + RangerGeolocationData b = RangerGeolocationData.create(new String[] {"1.1.1.4", "1.1.1.6", "B"}, 0, true); + RangerGeolocationData c = RangerGeolocationData.create(new String[] {"1.1.1.7", "1.1.1.9", "C"}, 0, true); + tree.insert(b); + tree.insert(a); + tree.insert(c); + + assertEquals("B", tree.find(RangerGeolocationData.ipAddressToLong("1.1.1.5")).getLocationData()[0]); + assertEquals("A", tree.find(RangerGeolocationData.ipAddressToLong("1.1.1.2")).getLocationData()[0]); + assertEquals("C", tree.find(RangerGeolocationData.ipAddressToLong("1.1.1.8")).getLocationData()[0]); + assertNull(tree.find(RangerGeolocationData.ipAddressToLong("1.1.1.10"))); + } + + @Test + public void test02_traversals_preOrder_and_inOrder() { + BinarySearchTree tree = new BinarySearchTree<>(); + RangerGeolocationData a = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.3", "A"}, 0, true); + RangerGeolocationData b = RangerGeolocationData.create(new String[] {"1.1.1.4", "1.1.1.6", "B"}, 0, true); + RangerGeolocationData c = RangerGeolocationData.create(new String[] {"1.1.1.0", "1.1.1.0", "C"}, 0, true); + tree.insert(a); + tree.insert(b); + tree.insert(c); + + List pre = new ArrayList<>(); + List in = new ArrayList<>(); + + tree.preOrderTraverseTree(value -> { + pre.add(value.getLocationData()[0]); + return value; + }); + tree.inOrderTraverseTree(value -> { + in.add(value.getLocationData()[0]); + return value; + }); + + assertEquals(3, pre.size()); + assertEquals(3, in.size()); + assertTrue(in.contains("A") && in.contains("B") && in.contains("C")); + } + + @Test + public void test03_rebalance_changes_structure_but_preserves_find() { + BinarySearchTree tree = new BinarySearchTree<>(); + for (int i = 0; i < 10; i++) { + String start = "10.0.0." + (i * 2 + 1); + String end = "10.0.0." + (i * 2 + 1); + RangerGeolocationData data = RangerGeolocationData.create(new String[] {start, end, "V" + i}, 0, true); + tree.insert(data); + } + + RangerGeolocationData before = tree.find(RangerGeolocationData.ipAddressToLong("10.0.0.1")); + assertNotNull(before); + + tree.rebalance(); + + RangerGeolocationData after = tree.find(RangerGeolocationData.ipAddressToLong("10.0.0.1")); + assertNotNull(after); + } + + @Test + public void test04_node_accessors_and_setRoot() { + BinarySearchTree tree = new BinarySearchTree<>(); + BinarySearchTree.Node node = new BinarySearchTree.Node<>(null); + tree.setRoot(node); + assertNotNull(tree.getRoot()); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestGeolocationMetadata.java b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestGeolocationMetadata.java new file mode 100644 index 0000000000..f698b5b2e3 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestGeolocationMetadata.java @@ -0,0 +1,75 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.geo; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestGeolocationMetadata { + @Test + public void test01_create_valid() { + String[] fields = new String[] {"FROM", "TO", "Country", "Region", "City"}; + GeolocationMetadata md = GeolocationMetadata.create(fields, 0); + assertNotNull(md); + assertEquals(3, md.getLocationDataItemNames().length); + assertEquals(0, md.getDataItemNameIndex("Country")); + assertEquals(1, md.getDataItemNameIndex("Region")); + assertEquals(2, md.getDataItemNameIndex("City")); + } + + @Test + public void test02_create_insufficient_returnsNull() { + String[] fields = new String[] {"FROM", "TO"}; + GeolocationMetadata md = GeolocationMetadata.create(fields, 10); + assertNull(md); + } + + @Test + public void test03_getDataItemNameIndex_blankOrAbsent() { + String[] fields = new String[] {"FROM", "TO", "Country"}; + GeolocationMetadata md = GeolocationMetadata.create(fields, 1); + assertEquals(-1, md.getDataItemNameIndex("")); + assertEquals(-1, md.getDataItemNameIndex("Missing")); + } + + @Test + public void test04_toString_containsHeadersAndNames() { + String[] fields = new String[] {"FROM", "TO", "Country", "Region"}; + GeolocationMetadata md = GeolocationMetadata.create(fields, 1); + String s = md.toString(); + assertTrue(s.contains("FROM_IP,TO_IP,")); + assertTrue(s.contains("Country")); + assertTrue(s.contains("Region")); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestRangerGeolocationData.java b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestRangerGeolocationData.java new file mode 100644 index 0000000000..a63d44ce0c --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestRangerGeolocationData.java @@ -0,0 +1,139 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.geo; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerGeolocationData { + @Test + public void test01_create_withDotFormat_validRange() { + String[] fields = new String[] {"1.1.1.1", "1.1.1.10", "US", "CA"}; + RangerGeolocationData data = RangerGeolocationData.create(fields, 0, true); + assertNotNull(data); + assertEquals(0, data.compareToRange(RangerGeolocationData.ipAddressToLong("1.1.1.5"))); + assertTrue(data.compareToRange(RangerGeolocationData.ipAddressToLong("1.1.1.0")) > 0); + assertTrue(data.compareToRange(RangerGeolocationData.ipAddressToLong("1.1.1.11")) < 0); + assertEquals(2, data.getLocationData().length); + } + + @Test + public void test02_create_withNumericFormat_validRange() { + // 10.0.0.1 -> 167772161, 10.0.0.10 -> 167772170 + String[] fields = new String[] {"167772161", "167772170", "IN", "KA"}; + RangerGeolocationData data = RangerGeolocationData.create(fields, 3, false); + assertNotNull(data); + assertEquals(0, data.compareToRange(RangerGeolocationData.ipAddressToLong("10.0.0.5"))); + } + + @Test + public void test03_create_insufficientFields_returnsNull() { + String[] fields = new String[] {"1.1.1.1", "1.1.1.2"}; + RangerGeolocationData data = RangerGeolocationData.create(fields, 1, true); + assertNull(data); + } + + @Test + public void test04_ipAddressToLong_and_unsignedIntToIPAddress_roundTrip() { + long val1 = RangerGeolocationData.ipAddressToLong("10.0.0.1"); + assertEquals(167772161L, val1); + String dotted1 = RangerGeolocationData.unsignedIntToIPAddress(val1); + assertEquals("10.0.0.1", dotted1); + + long val2 = RangerGeolocationData.ipAddressToLong("192.168.1.1"); + assertTrue(val2 < 0); + String dotted2 = RangerGeolocationData.unsignedIntToIPAddress(3232235777L); + assertEquals("192.168.1.1", dotted2); + } + + @Test + public void test05_unsignedIntToIPAddress_zeroOrNegative_returnsEmpty() { + assertEquals("", RangerGeolocationData.unsignedIntToIPAddress(0)); + assertEquals("", RangerGeolocationData.unsignedIntToIPAddress(-10)); + } + + @Test + public void test06_validateAsIP_dotNotationAndNumeric() { + assertTrue(RangerGeolocationData.validateAsIP("8.8.8.8", true)); + assertFalse(RangerGeolocationData.validateAsIP("999.999.999.999", true)); + assertTrue(RangerGeolocationData.validateAsIP("123456", false)); + assertFalse(RangerGeolocationData.validateAsIP("12a34", false)); + } + + @Test + public void test07_ipAddressToLong_invalidReturnsZero() { + assertEquals(0L, RangerGeolocationData.ipAddressToLong("not.an.ip")); + // IPv6 returns 0 as bytes length > 4 + assertEquals(0L, RangerGeolocationData.ipAddressToLong("2001:db8::1")); + } + + @Test + public void test08_compareTo_variousDifferences() { + RangerGeolocationData a = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.5", "US", null}, 0, true); + RangerGeolocationData b = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.5", "US", null}, 0, true); + RangerGeolocationData c = RangerGeolocationData.create(new String[] {"1.1.1.2", "1.1.1.6", "US", null}, 0, true); + RangerGeolocationData d = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.6", "US", null}, 0, true); + RangerGeolocationData e = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.5", "US"}, 0, true); + RangerGeolocationData f = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.5", "US", "AA"}, 0, true); + + assertTrue(a.compareTo(null) > 0); + assertEquals(0, a.compareTo(b)); + assertTrue(a.compareTo(c) < 0); + assertTrue(a.compareTo(d) < 0); + assertTrue(a.compareTo(e) > 0); + assertTrue(a.compareTo(f) != 0); + } + + @Test + public void test09_equals_and_hashCode_caching() { + RangerGeolocationData a = RangerGeolocationData.create(new String[] {"2.2.2.2", "2.2.2.3", "X"}, 0, true); + RangerGeolocationData b = RangerGeolocationData.create(new String[] {"2.2.2.2", "2.2.2.3", "X"}, 0, true); + assertTrue(a.equals(b)); + assertEquals(a.hashCode(), b.hashCode()); + int first = a.hashCode(); + int second = a.hashCode(); + assertEquals(first, second); + } + + @Test + public void test10_toString_containsFromToAndLocation() { + RangerGeolocationData a = RangerGeolocationData.create(new String[] {"3.3.3.3", "3.3.3.4", "Y", "Z"}, 0, true); + String s = a.toString(); + assertTrue(s.contains("from=3.3.3.3")); + assertTrue(s.contains("to=3.3.3.4")); + assertTrue(s.contains("Y")); + assertTrue(s.contains("Z")); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestRangerGeolocationDatabase.java b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestRangerGeolocationDatabase.java new file mode 100644 index 0000000000..8f94595b57 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestRangerGeolocationDatabase.java @@ -0,0 +1,105 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.geo; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerGeolocationDatabase { + @Test + public void test01_getValue_branches_and_valid() { + RangerGeolocationDatabase db = new RangerGeolocationDatabase(); + assertNull(db.getValue(null, "Country")); + assertNull(db.getValue(RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.1", "US"}, 0, true), "")); + + GeolocationMetadata md = GeolocationMetadata.create(new String[] {"FROM", "TO", "Country"}, 0); + db.setMetadata(md); + RangerGeolocationData data = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.1", "US"}, 0, true); + assertEquals("US", db.getValue(data, "Country")); + assertNull(db.getValue(data, "Region")); + + GeolocationMetadata md2 = GeolocationMetadata.create(new String[] {"FROM", "TO", "Country", "Region"}, 0); + db.setMetadata(md2); + RangerGeolocationData oneEntry = RangerGeolocationData.create(new String[] {"1.1.1.1", "1.1.1.1", "X"}, 0, true); + assertNull(db.getValue(oneEntry, "Region")); + } + + @Test + public void test02_find_and_optimize_and_dataMetadataAccessors() { + RangerGeolocationDatabase db = new RangerGeolocationDatabase(); + assertNull(db.find("")); + assertNull(db.find("not.an.ip")); + assertNull(db.find("2001:db8::1")); + + RangerGeolocationData data = RangerGeolocationData.create(new String[] {"2.2.2.2", "2.2.2.3", "Y"}, 0, true); + db.getData().insert(data); + assertEquals(data, db.find("2.2.2.2")); + + assertNotNull(db.getMetadata()); + db.setMetadata(null); + assertNotNull(db.getMetadata()); + + assertNotNull(db.getData()); + db.setData(null); + assertNotNull(db.getData()); + + db.optimize(); + } + + @Test + public void test03_dump_writesToFile() throws IOException { + RangerGeolocationDatabase db = new RangerGeolocationDatabase(); + GeolocationMetadata md = GeolocationMetadata.create(new String[] {"FROM", "TO", "Country", "Region"}, 0); + db.setMetadata(md); + RangerGeolocationData a = RangerGeolocationData.create(new String[] {"3.3.3.3", "3.3.3.4", "US", "CA"}, 0, true); + db.getData().insert(a); + + Path tmp = Files.createTempFile("geo-dump", ".txt"); + try { + ValuePrinter printer = new ValuePrinter<>(tmp.toAbsolutePath().toString()); + db.dump(printer); + String content = new String(Files.readAllBytes(tmp), StandardCharsets.UTF_8); + assertTrue(content.contains("Geolocation metadata")); + assertTrue(content.contains("Dump of geoDatabase")); + assertTrue(content.contains("from=3.3.3.3")); + } finally { + Files.deleteIfExists(tmp); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestValuePrinter.java b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestValuePrinter.java new file mode 100644 index 0000000000..d6d104528f --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/geo/TestValuePrinter.java @@ -0,0 +1,84 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.ranger.plugin.geo; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestValuePrinter { + @Test + public void test01_build_print_process_and_close_toFile() throws IOException { + Path tmp = Files.createTempFile("vp", ".txt"); + try { + ValuePrinter vp = new ValuePrinter<>(tmp.toAbsolutePath().toString()); + vp.build(); + vp.print("header"); + vp.process(new Object() { + public String toString() { + return "line"; + } + }); + vp.close(); + + String content = new String(Files.readAllBytes(tmp), StandardCharsets.UTF_8); + assertTrue(content.contains("header")); + assertTrue(content.contains("line")); + } finally { + Files.deleteIfExists(tmp); + } + } + + @Test + public void test02_build_withBlankFilename_then_print_process_close_noException() { + ValuePrinter vp = new ValuePrinter<>(null); + vp.build(); + vp.print("x"); + vp.process("y"); + vp.close(); + assertNotNull(vp); + } + + @Test + public void test03_build_failure_path_then_continue() { + ValuePrinter vp = new ValuePrinter<>("/root/ranger-unwritable.txt"); + vp.build(); + vp.print("x"); + vp.process("y"); + vp.close(); + assertNotNull(vp); + } +}