/** * 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.hadoop.hbase.security.visibility; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.apache.hadoop.hbase.testclassification.SecurityTests; import org.apache.hadoop.hbase.testclassification.SmallTests; import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode; import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode; import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode; import org.apache.hadoop.hbase.security.visibility.expression.Operator; import org.junit.Test; import org.junit.experimental.categories.Category; @Category({SecurityTests.class, SmallTests.class}) public class TestExpressionParser { private ExpressionParser parser = new ExpressionParser(); @Test public void testPositiveCases() throws Exception { // abc -> (abc) ExpressionNode node = parser.parse("abc"); assertTrue(node instanceof LeafExpressionNode); assertEquals("abc", ((LeafExpressionNode) node).getIdentifier()); // a&b|c&d -> (((a & b) | c) & ) node = parser.parse("a&b|c&d"); assertTrue(node instanceof NonLeafExpressionNode); NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("d", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("c", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); // (a) -> (a) node = parser.parse("(a)"); assertTrue(node instanceof LeafExpressionNode); assertEquals("a", ((LeafExpressionNode) node).getIdentifier()); // (a&b) -> (a & b) node = parser.parse(" ( a & b )"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); // ((((a&b)))) -> (a & b) node = parser.parse("((((a&b))))"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); // (a|b)&(cc|def) -> ((a | b) & (cc | def)) node = parser.parse("( a | b ) & (cc|def)"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); NonLeafExpressionNode nlNodeLeft = (NonLeafExpressionNode) nlNode.getChildExps().get(0); NonLeafExpressionNode nlNodeRight = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.OR, nlNodeLeft.getOperator()); assertEquals(2, nlNodeLeft.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNodeLeft.getChildExps().get(0)).getIdentifier()); assertEquals("b", ((LeafExpressionNode) nlNodeLeft.getChildExps().get(1)).getIdentifier()); assertEquals(Operator.OR, nlNodeRight.getOperator()); assertEquals(2, nlNodeRight.getChildExps().size()); assertEquals("cc", ((LeafExpressionNode) nlNodeRight.getChildExps().get(0)).getIdentifier()); assertEquals("def", ((LeafExpressionNode) nlNodeRight.getChildExps().get(1)).getIdentifier()); // a&(cc|de) -> (a & (cc | de)) node = parser.parse("a&(cc|de)"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("cc", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertEquals("de", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); // (a&b)|c -> ((a & b) | c) node = parser.parse("(a&b)|c"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("c", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); // (a&b&c)|d -> (((a & b) & c) | d) node = parser.parse("(a&b&c)|d"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("d", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("c", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); // a&(b|(c|d)) -> (a & (b | (c | d))) node = parser.parse("a&(b|(c|d))"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("c", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertEquals("d", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); // (!a) -> (!a) node = parser.parse("(!a)"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.NOT, nlNode.getOperator()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); // a&(!b) -> (a & (!b)) node = parser.parse("a&(!b)"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.NOT, nlNode.getOperator()); assertEquals(1, nlNode.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); // !a&b -> ((!a) & b) node = parser.parse("!a&b"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.NOT, nlNode.getOperator()); assertEquals(1, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); // !a&(!b) -> ((!a) & (!b)) node = parser.parse("!a&(!b)"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNodeLeft = (NonLeafExpressionNode) nlNode.getChildExps().get(0); nlNodeRight = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.NOT, nlNodeLeft.getOperator()); assertEquals(1, nlNodeLeft.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNodeLeft.getChildExps().get(0)).getIdentifier()); assertEquals(Operator.NOT, nlNodeRight.getOperator()); assertEquals(1, nlNodeRight.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNodeRight.getChildExps().get(0)).getIdentifier()); // !a&!b -> ((!a) & (!b)) node = parser.parse("!a&!b"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNodeLeft = (NonLeafExpressionNode) nlNode.getChildExps().get(0); nlNodeRight = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.NOT, nlNodeLeft.getOperator()); assertEquals(1, nlNodeLeft.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNodeLeft.getChildExps().get(0)).getIdentifier()); assertEquals(Operator.NOT, nlNodeRight.getOperator()); assertEquals(1, nlNodeRight.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNodeRight.getChildExps().get(0)).getIdentifier()); // !(a&b) -> (!(a & b)) node = parser.parse("!(a&b)"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.NOT, nlNode.getOperator()); assertEquals(1, nlNode.getChildExps().size()); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); // a&!b -> (a & (!b)) node = parser.parse("a&!b"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("a", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.NOT, nlNode.getOperator()); assertEquals(1, nlNode.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); // !((a|b)&!(c&!b)) -> (!((a | b) & (!(c & (!b))))) node = parser.parse("!((a | b) & !(c & !b))"); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.NOT, nlNode.getOperator()); assertEquals(1, nlNode.getChildExps().size()); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); assertTrue(nlNode.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNodeLeft = (NonLeafExpressionNode) nlNode.getChildExps().get(0); nlNodeRight = (NonLeafExpressionNode) nlNode.getChildExps().get(1); assertEquals(Operator.OR, nlNodeLeft.getOperator()); assertEquals("a", ((LeafExpressionNode) nlNodeLeft.getChildExps().get(0)).getIdentifier()); assertEquals("b", ((LeafExpressionNode) nlNodeLeft.getChildExps().get(1)).getIdentifier()); assertEquals(Operator.NOT, nlNodeRight.getOperator()); assertEquals(1, nlNodeRight.getChildExps().size()); nlNodeRight = (NonLeafExpressionNode) nlNodeRight.getChildExps().get(0); assertEquals(Operator.AND, nlNodeRight.getOperator()); assertEquals(2, nlNodeRight.getChildExps().size()); assertEquals("c", ((LeafExpressionNode) nlNodeRight.getChildExps().get(0)).getIdentifier()); assertTrue(nlNodeRight.getChildExps().get(1) instanceof NonLeafExpressionNode); nlNodeRight = (NonLeafExpressionNode) nlNodeRight.getChildExps().get(1); assertEquals(Operator.NOT, nlNodeRight.getOperator()); assertEquals(1, nlNodeRight.getChildExps().size()); assertEquals("b", ((LeafExpressionNode) nlNodeRight.getChildExps().get(0)).getIdentifier()); } @Test public void testNegativeCases() throws Exception { executeNegativeCase("("); executeNegativeCase(")"); executeNegativeCase("()"); executeNegativeCase("(a"); executeNegativeCase("a&"); executeNegativeCase("a&|b"); executeNegativeCase("!"); executeNegativeCase("a!"); executeNegativeCase("a!&"); executeNegativeCase("&"); executeNegativeCase("|"); executeNegativeCase("!(a|(b&c)&!b"); executeNegativeCase("!!a"); executeNegativeCase("( a & b ) | ( c & d e)"); executeNegativeCase("! a"); } @Test public void testNonAsciiCases() throws Exception { ExpressionNode node = parser.parse(CellVisibility.quote("\u0027") + "&" + CellVisibility.quote("\u002b") + "|" + CellVisibility.quote("\u002d") + "&" + CellVisibility.quote("\u003f")); assertTrue(node instanceof NonLeafExpressionNode); NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u003f", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u002d", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u002b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertEquals("\u0027", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); node = parser.parse(CellVisibility.quote("\u0027") + "&" + CellVisibility.quote("\u002b") + "|" + CellVisibility.quote("\u002d") + "&" + CellVisibility.quote("\u003f")); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u003f", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u002d", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u002b", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertEquals("\u0027", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); } @Test public void testCasesSeperatedByDoubleQuotes() throws Exception { ExpressionNode node = null; try { node = parser.parse("\u0027&\"|\u002b&\u003f"); fail("Excpetion must be thrown as there are special characters without quotes"); } catch (ParseException e) { } node = parser.parse(CellVisibility.quote("\u0027") + "&" + CellVisibility.quote("\"") + "|" + CellVisibility.quote("\u002b" + "&" + "\u003f")); assertTrue(node instanceof NonLeafExpressionNode); NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u002b" + "&" + "\u003f", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\"", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertEquals("\u0027", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); try { node = parser.parse(CellVisibility.quote("\u0027&\\") + "|" + CellVisibility.quote("\u002b" + "&" + "\\") + CellVisibility.quote("$$\"")); fail("Excpetion must be thrown as there is not operator"); } catch (ParseException e) { } node = parser.parse(CellVisibility.quote("\u0027" + "&" + "\\") + "|" + CellVisibility.quote("\u003f" + "&" + "\\") + "&" + CellVisibility.quote("$$\"")); assertTrue(node instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) node; assertEquals(Operator.AND, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("$$\"", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); assertTrue(nlNode.getChildExps().get(0) instanceof NonLeafExpressionNode); nlNode = (NonLeafExpressionNode) nlNode.getChildExps().get(0); assertEquals(Operator.OR, nlNode.getOperator()); assertEquals(2, nlNode.getChildExps().size()); assertEquals("\u0027" + "&" + "\\", ((LeafExpressionNode) nlNode.getChildExps().get(0)).getIdentifier()); assertEquals("\u003f" + "&" + "\\", ((LeafExpressionNode) nlNode.getChildExps().get(1)).getIdentifier()); try { node = parser.parse(CellVisibility.quote("\u002b&\\") + "|" + CellVisibility.quote("\u0027&\\") + "&" + "\"$$"); fail("Excpetion must be thrown as there is no end quote"); } catch (ParseException e) { } } private void executeNegativeCase(String exp) { try { parser.parse(exp); fail("Expected ParseException for expression " + exp); } catch (ParseException e) { } } }