1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.security.visibility;
19
20 import java.nio.charset.StandardCharsets;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Stack;
24
25 import org.apache.hadoop.hbase.classification.InterfaceAudience;
26 import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
27 import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
28 import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
29 import org.apache.hadoop.hbase.security.visibility.expression.Operator;
30 import org.apache.hadoop.hbase.util.Bytes;
31
32 @InterfaceAudience.Private
33 public class ExpressionParser {
34
35 private static final char CLOSE_PARAN = ')';
36 private static final char OPEN_PARAN = '(';
37 private static final char OR = '|';
38 private static final char AND = '&';
39 private static final char NOT = '!';
40 private static final char SPACE = ' ';
41 private static final char DOUBLE_QUOTES = '"';
42 public ExpressionNode parse(String expS) throws ParseException {
43 expS = expS.trim();
44 Stack<ExpressionNode> expStack = new Stack<ExpressionNode>();
45 int index = 0;
46 byte[] exp = Bytes.toBytes(expS);
47 int endPos = exp.length;
48 while (index < endPos) {
49 byte b = exp[index];
50 switch (b) {
51 case OPEN_PARAN:
52 processOpenParan(expStack, expS, index);
53 index = skipSpaces(exp, index);
54 break;
55 case CLOSE_PARAN:
56 processCloseParan(expStack, expS, index);
57 index = skipSpaces(exp, index);
58 break;
59 case AND:
60 case OR:
61 processANDorOROp(getOperator(b), expStack, expS, index);
62 index = skipSpaces(exp, index);
63 break;
64 case NOT:
65 processNOTOp(expStack, expS, index);
66 break;
67 case DOUBLE_QUOTES:
68 int labelOffset = ++index;
69
70
71
72 List<Byte> list = new ArrayList<Byte>();
73 while (index < endPos && !endDoubleQuotesFound(exp[index])) {
74 if (exp[index] == '\\') {
75 index++;
76 if (exp[index] != '\\' && exp[index] != '"')
77 throw new ParseException("invalid escaping with quotes " + expS + " at column : "
78 + index);
79 }
80 list.add(exp[index]);
81 index++;
82 }
83
84 if(index == endPos) {
85 throw new ParseException("No terminating quotes " + expS + " at column : " + index);
86 }
87
88
89
90 byte[] array = com.google.common.primitives.Bytes.toArray(list);
91 String leafExp = Bytes.toString(array).trim();
92 if (leafExp.isEmpty()) {
93 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
94 }
95 processLabelExpNode(new LeafExpressionNode(leafExp), expStack, expS, index);
96 index = skipSpaces(exp, index);
97 break;
98 default:
99 labelOffset = index;
100 do {
101 if (!VisibilityLabelsValidator.isValidAuthChar(exp[index])) {
102 throw new ParseException("Error parsing expression "
103 + expS + " at column : " + index);
104 }
105 index++;
106 } while (index < endPos && !isEndOfLabel(exp[index]));
107 leafExp = new String(exp, labelOffset, index - labelOffset,
108 StandardCharsets.UTF_8).trim();
109 if (leafExp.isEmpty()) {
110 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
111 }
112 processLabelExpNode(new LeafExpressionNode(leafExp), expStack, expS, index);
113
114 index--;
115 index = skipSpaces(exp, index);
116 }
117 index++;
118 }
119 if (expStack.size() != 1) {
120 throw new ParseException("Error parsing expression " + expS);
121 }
122 ExpressionNode top = expStack.pop();
123 if (top == LeafExpressionNode.OPEN_PARAN_NODE) {
124 throw new ParseException("Error parsing expression " + expS);
125 }
126 if (top instanceof NonLeafExpressionNode) {
127 NonLeafExpressionNode nlTop = (NonLeafExpressionNode) top;
128 if (nlTop.getOperator() == Operator.NOT) {
129 if (nlTop.getChildExps().size() != 1) {
130 throw new ParseException("Error parsing expression " + expS);
131 }
132 } else if (nlTop.getChildExps().size() != 2) {
133 throw new ParseException("Error parsing expression " + expS);
134 }
135 }
136 return top;
137 }
138
139 private int skipSpaces(byte[] exp, int index) {
140 while (index < exp.length -1 && exp[index+1] == SPACE) {
141 index++;
142 }
143 return index;
144 }
145
146 private void processCloseParan(Stack<ExpressionNode> expStack, String expS, int index)
147 throws ParseException {
148 if (expStack.size() < 2) {
149
150
151 throw new ParseException();
152 } else {
153 ExpressionNode top = expStack.pop();
154 ExpressionNode secondTop = expStack.pop();
155
156
157 if (top == LeafExpressionNode.OPEN_PARAN_NODE
158 || secondTop != LeafExpressionNode.OPEN_PARAN_NODE) {
159 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
160 }
161
162
163
164
165 if (top instanceof NonLeafExpressionNode) {
166 NonLeafExpressionNode nlTop = (NonLeafExpressionNode) top;
167 if ((nlTop.getOperator() == Operator.NOT && nlTop.getChildExps().size() != 1)
168 || (nlTop.getOperator() != Operator.NOT && nlTop.getChildExps().size() != 2)) {
169 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
170 }
171 }
172
173
174
175
176
177 if (!expStack.isEmpty()) {
178 ExpressionNode thirdTop = expStack.peek();
179 if (thirdTop instanceof NonLeafExpressionNode) {
180 NonLeafExpressionNode nlThirdTop = (NonLeafExpressionNode) expStack.pop();
181 nlThirdTop.addChildExp(top);
182 if (nlThirdTop.getOperator() == Operator.NOT) {
183
184
185
186 if (!expStack.isEmpty()) {
187 ExpressionNode fourthTop = expStack.peek();
188 if (fourthTop instanceof NonLeafExpressionNode) {
189
190 NonLeafExpressionNode nlFourthTop = (NonLeafExpressionNode) fourthTop;
191 assert nlFourthTop.getOperator() != Operator.NOT;
192
193 assert nlFourthTop.getChildExps().size() == 1;
194 nlFourthTop.addChildExp(nlThirdTop);
195 return;
196 }
197 }
198 }
199 top = nlThirdTop;
200 }
201 }
202 expStack.push(top);
203 }
204 }
205
206 private void processOpenParan(Stack<ExpressionNode> expStack, String expS, int index)
207 throws ParseException {
208 if (!expStack.isEmpty()) {
209 ExpressionNode top = expStack.peek();
210
211 if (top instanceof LeafExpressionNode && top != LeafExpressionNode.OPEN_PARAN_NODE) {
212 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
213 } else if (top instanceof NonLeafExpressionNode) {
214
215
216
217
218
219 NonLeafExpressionNode nlTop = (NonLeafExpressionNode) top;
220 if ((nlTop.getOperator() == Operator.NOT && nlTop.getChildExps().size() != 0)
221 || (nlTop.getOperator() != Operator.NOT && nlTop.getChildExps().size() != 1)) {
222 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
223 }
224 }
225 }
226 expStack.push(LeafExpressionNode.OPEN_PARAN_NODE);
227 }
228
229 private void processLabelExpNode(LeafExpressionNode node, Stack<ExpressionNode> expStack,
230 String expS, int index) throws ParseException {
231 if (expStack.isEmpty()) {
232 expStack.push(node);
233 } else {
234 ExpressionNode top = expStack.peek();
235 if (top == LeafExpressionNode.OPEN_PARAN_NODE) {
236 expStack.push(node);
237 } else if (top instanceof NonLeafExpressionNode) {
238 NonLeafExpressionNode nlTop = (NonLeafExpressionNode) expStack.pop();
239 nlTop.addChildExp(node);
240 if (nlTop.getOperator() == Operator.NOT && !expStack.isEmpty()) {
241 ExpressionNode secondTop = expStack.peek();
242 if (secondTop == LeafExpressionNode.OPEN_PARAN_NODE) {
243 expStack.push(nlTop);
244 } else if (secondTop instanceof NonLeafExpressionNode) {
245 ((NonLeafExpressionNode) secondTop).addChildExp(nlTop);
246 }
247 } else {
248 expStack.push(nlTop);
249 }
250 } else {
251 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
252 }
253 }
254 }
255
256 private void processANDorOROp(Operator op, Stack<ExpressionNode> expStack, String expS, int index)
257 throws ParseException {
258 if (expStack.isEmpty()) {
259 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
260 }
261 ExpressionNode top = expStack.pop();
262 if (top.isSingleNode()) {
263 if (top == LeafExpressionNode.OPEN_PARAN_NODE) {
264 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
265 }
266 expStack.push(new NonLeafExpressionNode(op, top));
267 } else {
268 NonLeafExpressionNode nlTop = (NonLeafExpressionNode) top;
269 if (nlTop.getChildExps().size() != 2) {
270 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
271 }
272 expStack.push(new NonLeafExpressionNode(op, nlTop));
273 }
274 }
275
276 private void processNOTOp(Stack<ExpressionNode> expStack, String expS, int index)
277 throws ParseException {
278
279
280
281 if (!expStack.isEmpty()) {
282 ExpressionNode top = expStack.peek();
283 if (top.isSingleNode() && top != LeafExpressionNode.OPEN_PARAN_NODE) {
284 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
285 }
286 if (!top.isSingleNode() && ((NonLeafExpressionNode) top).getChildExps().size() != 1) {
287 throw new ParseException("Error parsing expression " + expS + " at column : " + index);
288 }
289 }
290 expStack.push(new NonLeafExpressionNode(Operator.NOT));
291 }
292
293 private static boolean endDoubleQuotesFound(byte b) {
294 return (b == DOUBLE_QUOTES);
295 }
296 private static boolean isEndOfLabel(byte b) {
297 return (b == OPEN_PARAN || b == CLOSE_PARAN || b == OR || b == AND ||
298 b == NOT || b == SPACE);
299 }
300
301 private static Operator getOperator(byte op) {
302 switch (op) {
303 case AND:
304 return Operator.AND;
305 case OR:
306 return Operator.OR;
307 case NOT:
308 return Operator.NOT;
309 }
310 return null;
311 }
312 }