001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.security.visibility;
019
020import java.util.List;
021import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
022import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
023import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
024import org.apache.hadoop.hbase.security.visibility.expression.Operator;
025import org.apache.yetus.audience.InterfaceAudience;
026
027@InterfaceAudience.Private
028public class ExpressionExpander {
029
030  public ExpressionNode expand(ExpressionNode src) {
031    if (!src.isSingleNode()) {
032      NonLeafExpressionNode nlExp = (NonLeafExpressionNode) src;
033      List<ExpressionNode> childExps = nlExp.getChildExps();
034      Operator outerOp = nlExp.getOperator();
035      if (isToBeExpanded(childExps)) {
036        // Any of the child exp is a non leaf exp with & or | operator
037        NonLeafExpressionNode newNode = new NonLeafExpressionNode(nlExp.getOperator());
038        for (ExpressionNode exp : childExps) {
039          if (exp.isSingleNode()) {
040            newNode.addChildExp(exp);
041          } else {
042            newNode.addChildExp(expand(exp));
043          }
044        }
045        nlExp = expandNonLeaf(newNode, outerOp);
046      }
047      return nlExp;
048    }
049    if (
050      src instanceof NonLeafExpressionNode
051        && ((NonLeafExpressionNode) src).getOperator() == Operator.NOT
052    ) {
053      // Negate the exp
054      return negate((NonLeafExpressionNode) src);
055    }
056    return src;
057  }
058
059  private ExpressionNode negate(NonLeafExpressionNode nlExp) {
060    ExpressionNode notChild = nlExp.getChildExps().get(0);
061    if (notChild instanceof LeafExpressionNode) {
062      return nlExp;
063    }
064    NonLeafExpressionNode nlNotChild = (NonLeafExpressionNode) notChild;
065    if (nlNotChild.getOperator() == Operator.NOT) {
066      // negate the negate
067      return nlNotChild.getChildExps().get(0);
068    }
069    Operator negateOp = nlNotChild.getOperator() == Operator.AND ? Operator.OR : Operator.AND;
070    NonLeafExpressionNode newNode = new NonLeafExpressionNode(negateOp);
071    for (ExpressionNode expNode : nlNotChild.getChildExps()) {
072      NonLeafExpressionNode negateNode = new NonLeafExpressionNode(Operator.NOT);
073      negateNode.addChildExp(expNode.deepClone());
074      newNode.addChildExp(expand(negateNode));
075    }
076    return newNode;
077  }
078
079  private boolean isToBeExpanded(List<ExpressionNode> childExps) {
080    for (ExpressionNode exp : childExps) {
081      if (!exp.isSingleNode()) {
082        return true;
083      }
084    }
085    return false;
086  }
087
088  private NonLeafExpressionNode expandNonLeaf(NonLeafExpressionNode newNode, Operator outerOp) {
089    // Now go for the merge or expansion across brackets
090    List<ExpressionNode> newChildExps = newNode.getChildExps();
091    assert newChildExps.size() == 2;
092    ExpressionNode leftChild = newChildExps.get(0);
093    ExpressionNode rightChild = newChildExps.get(1);
094    if (rightChild.isSingleNode()) {
095      // Merge the single right node into the left side
096      assert leftChild instanceof NonLeafExpressionNode;
097      newNode = mergeChildNodes(newNode, outerOp, rightChild, (NonLeafExpressionNode) leftChild);
098    } else if (leftChild.isSingleNode()) {
099      // Merge the single left node into the right side
100      assert rightChild instanceof NonLeafExpressionNode;
101      newNode = mergeChildNodes(newNode, outerOp, leftChild, (NonLeafExpressionNode) rightChild);
102    } else {
103      // Both the child exp nodes are non single.
104      NonLeafExpressionNode leftChildNLE = (NonLeafExpressionNode) leftChild;
105      NonLeafExpressionNode rightChildNLE = (NonLeafExpressionNode) rightChild;
106      if (outerOp == leftChildNLE.getOperator() && outerOp == rightChildNLE.getOperator()) {
107        // Merge
108        NonLeafExpressionNode leftChildNLEClone = leftChildNLE.deepClone();
109        leftChildNLEClone.addChildExps(rightChildNLE.getChildExps());
110        newNode = leftChildNLEClone;
111      } else {
112        // (a | b) & (c & d) ...
113        if (outerOp == Operator.OR) {
114          // (a | b) | (c & d)
115          if (
116            leftChildNLE.getOperator() == Operator.OR && rightChildNLE.getOperator() == Operator.AND
117          ) {
118            leftChildNLE.addChildExp(rightChildNLE);
119            newNode = leftChildNLE;
120          } else if (
121            leftChildNLE.getOperator() == Operator.AND && rightChildNLE.getOperator() == Operator.OR
122          ) {
123            // (a & b) | (c | d)
124            rightChildNLE.addChildExp(leftChildNLE);
125            newNode = rightChildNLE;
126          }
127          // (a & b) | (c & d)
128          // This case no need to do any thing
129        } else {
130          // outer op is &
131          // (a | b) & (c & d) => (a & c & d) | (b & c & d)
132          if (
133            leftChildNLE.getOperator() == Operator.OR && rightChildNLE.getOperator() == Operator.AND
134          ) {
135            newNode = new NonLeafExpressionNode(Operator.OR);
136            for (ExpressionNode exp : leftChildNLE.getChildExps()) {
137              NonLeafExpressionNode rightChildNLEClone = rightChildNLE.deepClone();
138              rightChildNLEClone.addChildExp(exp);
139              newNode.addChildExp(rightChildNLEClone);
140            }
141          } else if (
142            leftChildNLE.getOperator() == Operator.AND && rightChildNLE.getOperator() == Operator.OR
143          ) {
144            // (a & b) & (c | d) => (a & b & c) | (a & b & d)
145            newNode = new NonLeafExpressionNode(Operator.OR);
146            for (ExpressionNode exp : rightChildNLE.getChildExps()) {
147              NonLeafExpressionNode leftChildNLEClone = leftChildNLE.deepClone();
148              leftChildNLEClone.addChildExp(exp);
149              newNode.addChildExp(leftChildNLEClone);
150            }
151          } else {
152            // (a | b) & (c | d) => (a & c) | (a & d) | (b & c) | (b & d)
153            newNode = new NonLeafExpressionNode(Operator.OR);
154            for (ExpressionNode leftExp : leftChildNLE.getChildExps()) {
155              for (ExpressionNode rightExp : rightChildNLE.getChildExps()) {
156                NonLeafExpressionNode newChild = new NonLeafExpressionNode(Operator.AND);
157                newChild.addChildExp(leftExp.deepClone());
158                newChild.addChildExp(rightExp.deepClone());
159                newNode.addChildExp(newChild);
160              }
161            }
162          }
163        }
164      }
165    }
166    return newNode;
167  }
168
169  private NonLeafExpressionNode mergeChildNodes(NonLeafExpressionNode newOuterNode,
170    Operator outerOp, ExpressionNode lChild, NonLeafExpressionNode nlChild) {
171    // Merge the single right/left node into the other side
172    if (nlChild.getOperator() == outerOp) {
173      NonLeafExpressionNode leftChildNLEClone = nlChild.deepClone();
174      leftChildNLEClone.addChildExp(lChild);
175      newOuterNode = leftChildNLEClone;
176    } else if (outerOp == Operator.AND) {
177      assert nlChild.getOperator() == Operator.OR;
178      // outerOp is & here. We need to expand the node here
179      // (a | b) & c -> (a & c) | (b & c)
180      // OR
181      // c & (a | b) -> (c & a) | (c & b)
182      newOuterNode = new NonLeafExpressionNode(Operator.OR);
183      for (ExpressionNode exp : nlChild.getChildExps()) {
184        newOuterNode.addChildExp(new NonLeafExpressionNode(Operator.AND, exp, lChild));
185      }
186    }
187    return newOuterNode;
188  }
189}