/* * 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 static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.junit.Assert; import org.junit.Test; import com.google.common.collect.Lists; public class RangerTimeOfDayMatcherTest { final Pattern p = RangerTimeOfDayMatcher._Pattern; @Test public void test_patterMatching_happyPath() { // sensible values and some goofy ones work String[] durations = new String[] { "9am-5pm", " 9Am -5 Pm", " 9Am -5 Pm", "9 AM -5 p.m.", "9a.M - 5Pm.", "9:30am-5:30pm", " 9:00Am -5:59 Pm", " 9 am - 4 pm ", "9pm-5AM", "11am-12pm", "11pm-12am", "12am-12pm", "12pm-12am" }; check(durations, true); } @Test public void test_patterMatching_unexpectedMatches() { // even semantically illegal durations work -- parsing is just for format not for semantics! String[] durations = new String[] { "00PM-44PM", // any two digits are allowed! "9:0am-5:7pm", // Minute part can be one digit }; check(durations, true); } @Test public void test_patterMatching_misMatches() { // clearly invalid values don't match. String[] durations = new String[] { "9am", "-", "", "9-5", "9-10am", "9pm-6", "009AM-5AM", // only 2 digits allowed "9am-5p m", // space betweem am or pm are not allowed "9:am-5:30pm", // if there is a : then minutes part must follow! "9:00am-5:300pm", // Minutes part is limited to 2 digits "9: 00am-5 :300pm", // No space allowed around the : }; check(durations, false); } void check(String[] durations, boolean match) { for (String aDuration : durations) { Matcher matcher = p.matcher(aDuration); if (match) { Assert.assertTrue(aDuration, matcher.matches()); } else { Assert.assertFalse(aDuration, matcher.matches()); } } } @Test public void test_patterMatching_happyPath_groups() { // groups returned by matcher are right String[][] input = new String[][] { { "9am-5pm", "9", null, "a", "5", null, "p"}, { "9Am -5 pM", "9", null, "A", "5", null, "p"}, { "9 AM -5 p.m.", "9", null, "A", "5", null, "p" }, { "9:30AM - 5:15pm", "9", "30", "A", "5", "15", "p" }, { "9:30 AM - 5:15 p.m.", "9", "30", "A", "5", "15", "p" }, { "9pm-5am", "9", null, "p", "5", null, "a"}, { "11am-12pm", "11", null, "a", "12", null, "p" }, { "11pm-12am", "11", null, "p", "12", null, "a" }, { "12am-12pm", "12", null, "a", "12", null, "p" }, { "12pm-12am", "12", null, "p", "12", null, "a" }, }; checkGroups(input); } void checkGroups(String[][] input) { for (String[] data : input) { Matcher m = p.matcher(data[0]); Assert.assertTrue(data[0], m.matches()); Assert.assertEquals(8, m.groupCount()); Assert.assertEquals(data[1], m.group(1)); Assert.assertEquals(data[2], m.group(3)); Assert.assertEquals(data[3], m.group(4)); Assert.assertEquals(data[4], m.group(5)); Assert.assertEquals(data[5], m.group(7)); Assert.assertEquals(data[6], m.group(8)); } } @Test public void test_ExtractDuration_happyPath() { RangerTimeOfDayMatcher matcher = new RangerTimeOfDayMatcher(); Object[][] input = new Object[][] { { "9am-5pm", true, 9*60, (12+5)*60 }, { "1 PM - 10P.M.", true, (12+1)*60, (12+10)*60 }, { "1PM - 9AM", true, (12+1)*60, 9*60 }, { "11am-12pm", true, 11*60, 12*60 }, { "11pm-12am", true, (12+11)*60, 0*60 }, { "12am-12pm", true, 0*60, 12*60 }, { "12pm-12am", true, 12*60, 0*60 }, { "1PM", false, null, null }, // illegal patterns should come back as null, too }; for (Object[] data: input) { int[] duration = matcher.extractDuration((String)data[0]); boolean expectedToMatch = (boolean)data[1]; if (expectedToMatch) { int start = (Integer)data[2]; int end = (Integer)data[3]; Assert.assertArrayEquals(new int[] { start, end }, duration); } else { Assert.assertNull(duration); } } } @Test public void test_durationsMatched() { List<int[]> durations = Lists.newArrayList( new int[]{2*60, 7*60}, // 2am-7am new int[]{9*60, 17*60}); // 9am-5pm RangerTimeOfDayMatcher matcher = new RangerTimeOfDayMatcher(); Object[][] input = new Object[][] { { 1, false }, { 2, true }, { 3, true }, { 7, true }, { 8, false }, {9, true }, {10, true }, {16, true}, {17, true}, {18, false }, {23, false }, }; for (Object[] data : input) { int hour = (int)data[0]; boolean matchExpected = (boolean)data[1]; boolean result = matcher.durationMatched(durations, hour, 0); if (matchExpected) { Assert.assertTrue("" + hour, result); } else { Assert.assertFalse("" + hour, result); } } } @Test public void test_end2end_happyPath() { RangerPolicyItemCondition itemCondition = mock(RangerPolicyItemCondition.class); when(itemCondition.getValues()).thenReturn(Arrays.asList("2:45a.m. -7:00 AM", " 9:15AM- 5:30P.M. ", "11pm-2am")); RangerTimeOfDayMatcher matcher = new RangerTimeOfDayMatcher(); matcher.setConditionDef(null); matcher.setPolicyItemCondition(itemCondition); matcher.init(); Object[][] input = new Object[][] { { 1, 0, true }, { 2, 0, true }, { 2, 1, false }, { 2, 44, false }, { 2, 45, true }, { 3, 0, true }, { 7, 0, true }, { 7, 01, false }, { 8, 0, false }, { 9, 15, true }, {10, 0, true }, {17, 0, true}, {17, 30, true}, {17, 31, false}, {18, 0, false }, {22, 59, false }, {23, 0, true }, }; RangerAccessRequest request = mock(RangerAccessRequest.class); for (Object[] data : input) { int hour = (int)data[0]; int minute = (int)data[1]; Calendar c = new GregorianCalendar(2015, Calendar.APRIL, 1, hour, minute); Date aDate = c.getTime(); when(request.getAccessTime()).thenReturn(aDate); boolean matchExpected = (boolean)data[2]; if (matchExpected) { Assert.assertTrue("" + hour, matcher.isMatched(request)); } else { Assert.assertFalse("" + hour, matcher.isMatched(request)); } } } @Test public void test_end2end_happyPath_12_oClock() { RangerPolicyItemCondition itemCondition = mock(RangerPolicyItemCondition.class); when(itemCondition.getValues()).thenReturn(Arrays.asList("12am-1am", "11am-12pm", "12pm-1pm", "11pm-12am")); RangerTimeOfDayMatcher matcher = new RangerTimeOfDayMatcher(); matcher.setConditionDef(null); matcher.setPolicyItemCondition(itemCondition); matcher.init(); Object[][] input = new Object[][] { { 0, 00, true }, { 0, 01, true }, { 1, 00, true }, { 1, 01, false }, { 10, 59, false }, { 11, 00, true }, { 11, 59, true }, { 12, 00, true }, { 12, 01, true }, { 12, 59, true }, { 13, 00, true }, { 13, 01, false }, {22, 59, false }, {23, 0, true }, {23, 59, true }, }; RangerAccessRequest request = mock(RangerAccessRequest.class); for (Object[] data : input) { int hour = (int)data[0]; int minute = (int)data[1]; Calendar c = new GregorianCalendar(2015, Calendar.APRIL, 1, hour, minute); Date aDate = c.getTime(); when(request.getAccessTime()).thenReturn(aDate); boolean matchExpected = (boolean)data[2]; if (matchExpected) { Assert.assertTrue("" + hour, matcher.isMatched(request)); } else { Assert.assertFalse("" + hour, matcher.isMatched(request)); } } } }