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 static org.apache.hadoop.hbase.TagType.VISIBILITY_TAG_TYPE;
21 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
22 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
23 import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.DataOutputStream;
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Set;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.hbase.AuthUtil;
39 import org.apache.hadoop.hbase.Cell;
40 import org.apache.hadoop.hbase.CellUtil;
41 import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
42 import org.apache.hadoop.hbase.Tag;
43 import org.apache.hadoop.hbase.TagType;
44 import org.apache.hadoop.hbase.classification.InterfaceAudience;
45 import org.apache.hadoop.hbase.client.Delete;
46 import org.apache.hadoop.hbase.client.Get;
47 import org.apache.hadoop.hbase.client.HTable;
48 import org.apache.hadoop.hbase.client.Put;
49 import org.apache.hadoop.hbase.client.Result;
50 import org.apache.hadoop.hbase.client.Table;
51 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
52 import org.apache.hadoop.hbase.regionserver.OperationStatus;
53 import org.apache.hadoop.hbase.regionserver.Region;
54 import org.apache.hadoop.hbase.security.Superusers;
55 import org.apache.hadoop.hbase.security.User;
56 import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
57 import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
58 import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
59 import org.apache.hadoop.hbase.security.visibility.expression.Operator;
60 import org.apache.hadoop.hbase.util.Bytes;
61
62
63
64
65
66
67
68 @InterfaceAudience.Private
69 public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelService {
70
71 private static final Log LOG = LogFactory.getLog(ExpAsStringVisibilityLabelServiceImpl.class);
72
73 private static final byte[] DUMMY_VALUE = new byte[0];
74 private static final byte STRING_SERIALIZATION_FORMAT = 2;
75 private static final Tag STRING_SERIALIZATION_FORMAT_TAG = new Tag(
76 TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE,
77 new byte[] { STRING_SERIALIZATION_FORMAT });
78 private final ExpressionParser expressionParser = new ExpressionParser();
79 private final ExpressionExpander expressionExpander = new ExpressionExpander();
80 private Configuration conf;
81 private Region labelsRegion;
82 private List<ScanLabelGenerator> scanLabelGenerators;
83
84 @Override
85 public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
86
87
88
89 OperationStatus[] status = new OperationStatus[labels.size()];
90 for (int i = 0; i < labels.size(); i++) {
91 status[i] = new OperationStatus(OperationStatusCode.SUCCESS);
92 }
93 return status;
94 }
95
96 @Override
97 public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
98 assert labelsRegion != null;
99 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
100 Put p = new Put(user);
101 for (byte[] auth : authLabels) {
102 p.addImmutable(LABELS_TABLE_FAMILY, auth, DUMMY_VALUE);
103 }
104 this.labelsRegion.put(p);
105
106 for (int i = 0; i < authLabels.size(); i++) {
107 finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
108 }
109 return finalOpStatus;
110 }
111
112 @Override
113 public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
114 assert labelsRegion != null;
115 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
116 List<String> currentAuths;
117 if (AuthUtil.isGroupPrincipal(Bytes.toString(user))) {
118 String group = AuthUtil.getGroupName(Bytes.toString(user));
119 currentAuths = this.getGroupAuths(new String[]{group}, true);
120 }
121 else {
122 currentAuths = this.getUserAuths(user, true);
123 }
124 Delete d = new Delete(user);
125 int i = 0;
126 for (byte[] authLabel : authLabels) {
127 String authLabelStr = Bytes.toString(authLabel);
128 if (currentAuths.contains(authLabelStr)) {
129 d.deleteColumns(LABELS_TABLE_FAMILY, authLabel);
130 } else {
131
132 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
133 new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
134 + Bytes.toString(user)));
135 }
136 i++;
137 }
138 this.labelsRegion.delete(d);
139
140 for (i = 0; i < authLabels.size(); i++) {
141 if (finalOpStatus[i] == null) {
142 finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
143 }
144 }
145 return finalOpStatus;
146 }
147
148 @Override
149 @Deprecated
150 public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
151 return getUserAuths(user, systemCall);
152 }
153
154 @Override
155 public List<String> getUserAuths(byte[] user, boolean systemCall) throws IOException {
156 assert (labelsRegion != null || systemCall);
157 List<String> auths = new ArrayList<String>();
158 Get get = new Get(user);
159 List<Cell> cells = null;
160 if (labelsRegion == null) {
161 Table table = null;
162 try {
163 table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
164 Result result = table.get(get);
165 cells = result.listCells();
166 } finally {
167 if (table != null) {
168 table.close();
169 }
170 }
171 } else {
172 cells = this.labelsRegion.get(get, false);
173 }
174 if (cells != null) {
175 for (Cell cell : cells) {
176 String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
177 cell.getQualifierLength());
178 auths.add(auth);
179 }
180 }
181 return auths;
182 }
183
184 @Override
185 public List<String> getGroupAuths(String[] groups, boolean systemCall) throws IOException {
186 assert (labelsRegion != null || systemCall);
187 List<String> auths = new ArrayList<String>();
188 if (groups != null && groups.length > 0) {
189 for (String group : groups) {
190 Get get = new Get(Bytes.toBytes(AuthUtil.toGroupEntry(group)));
191 List<Cell> cells = null;
192 if (labelsRegion == null) {
193 Table table = null;
194 try {
195 table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
196 Result result = table.get(get);
197 cells = result.listCells();
198 } finally {
199 if (table != null) {
200 table.close();
201 }
202 }
203 } else {
204 cells = this.labelsRegion.get(get, false);
205 }
206 if (cells != null) {
207 for (Cell cell : cells) {
208 String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
209 cell.getQualifierLength());
210 auths.add(auth);
211 }
212 }
213 }
214 }
215 return auths;
216 }
217
218 @Override
219 public List<String> listLabels(String regex) throws IOException {
220
221 return new ArrayList<String>();
222 }
223
224 @Override
225 public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
226 boolean checkAuths) throws IOException {
227 ExpressionNode node = null;
228 try {
229 node = this.expressionParser.parse(visExpression);
230 } catch (ParseException e) {
231 throw new IOException(e);
232 }
233 node = this.expressionExpander.expand(node);
234 List<Tag> tags = new ArrayList<Tag>();
235 if (withSerializationFormat) {
236 tags.add(STRING_SERIALIZATION_FORMAT_TAG);
237 }
238 if (node instanceof NonLeafExpressionNode
239 && ((NonLeafExpressionNode) node).getOperator() == Operator.OR) {
240 for (ExpressionNode child : ((NonLeafExpressionNode) node).getChildExps()) {
241 tags.add(createTag(child));
242 }
243 } else {
244 tags.add(createTag(node));
245 }
246 return tags;
247 }
248
249 @Override
250 public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
251 throws IOException {
252
253
254 if (isReadFromSystemAuthUser()) {
255 return new VisibilityExpEvaluator() {
256 @Override
257 public boolean evaluate(Cell cell) throws IOException {
258 return true;
259 }
260 };
261 }
262 List<String> authLabels = null;
263 for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
264 try {
265
266 authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
267 authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
268 authorizations = new Authorizations(authLabels);
269 } catch (Throwable t) {
270 LOG.error(t);
271 throw new IOException(t);
272 }
273 }
274 final List<String> authLabelsFinal = authLabels;
275 return new VisibilityExpEvaluator() {
276 @Override
277 public boolean evaluate(Cell cell) throws IOException {
278 boolean visibilityTagPresent = false;
279
280 if (cell.getTagsLength() > 0) {
281 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
282 cell.getTagsLength());
283 while (tagsItr.hasNext()) {
284 boolean includeKV = true;
285 Tag tag = tagsItr.next();
286 if (tag.getType() == VISIBILITY_TAG_TYPE) {
287 visibilityTagPresent = true;
288 int offset = tag.getTagOffset();
289 int endOffset = offset + tag.getTagLength();
290 while (offset < endOffset) {
291 short len = Bytes.toShort(tag.getBuffer(), offset);
292 offset += 2;
293 if (len < 0) {
294
295 len = (short) (-1 * len);
296 String label = Bytes.toString(tag.getBuffer(), offset, len);
297 if (authLabelsFinal.contains(label)) {
298 includeKV = false;
299 break;
300 }
301 } else {
302 String label = Bytes.toString(tag.getBuffer(), offset, len);
303 if (!authLabelsFinal.contains(label)) {
304 includeKV = false;
305 break;
306 }
307 }
308 offset += len;
309 }
310 if (includeKV) {
311
312
313
314 return true;
315 }
316 }
317 }
318 }
319 return !(visibilityTagPresent);
320 }
321 };
322 }
323
324 protected boolean isReadFromSystemAuthUser() throws IOException {
325 User user = VisibilityUtils.getActiveUser();
326 return havingSystemAuth(user);
327 }
328
329 private Tag createTag(ExpressionNode node) throws IOException {
330 ByteArrayOutputStream baos = new ByteArrayOutputStream();
331 DataOutputStream dos = new DataOutputStream(baos);
332 List<String> labels = new ArrayList<String>();
333 List<String> notLabels = new ArrayList<String>();
334 extractLabels(node, labels, notLabels);
335 Collections.sort(labels);
336 Collections.sort(notLabels);
337
338
339
340
341 for (String label : notLabels) {
342 byte[] bLabel = Bytes.toBytes(label);
343 short length = (short) bLabel.length;
344 length = (short) (-1 * length);
345 dos.writeShort(length);
346 dos.write(bLabel);
347 }
348 for (String label : labels) {
349 byte[] bLabel = Bytes.toBytes(label);
350 dos.writeShort(bLabel.length);
351 dos.write(bLabel);
352 }
353 return new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
354 }
355
356 private void extractLabels(ExpressionNode node, List<String> labels, List<String> notLabels) {
357 if (node.isSingleNode()) {
358 if (node instanceof NonLeafExpressionNode) {
359
360 LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node)
361 .getChildExps().get(0);
362 notLabels.add(lNode.getIdentifier());
363 } else {
364 labels.add(((LeafExpressionNode) node).getIdentifier());
365 }
366 } else {
367
368 NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
369 assert nlNode.getOperator() == Operator.AND;
370 List<ExpressionNode> childExps = nlNode.getChildExps();
371 for (ExpressionNode child : childExps) {
372 extractLabels(child, labels, notLabels);
373 }
374 }
375 }
376
377 @Override
378 public Configuration getConf() {
379 return this.conf;
380 }
381
382 @Override
383 public void setConf(Configuration conf) {
384 this.conf = conf;
385 }
386
387 @Override
388 public void init(RegionCoprocessorEnvironment e) throws IOException {
389 this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
390 if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
391 this.labelsRegion = e.getRegion();
392 }
393 }
394
395 @Override
396 @Deprecated
397 public boolean havingSystemAuth(byte[] user) throws IOException {
398
399 if (Superusers.isSuperUser(Bytes.toString(user))) {
400 return true;
401 }
402 List<String> auths = this.getUserAuths(user, true);
403 if (LOG.isTraceEnabled()) {
404 LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths);
405 }
406 return auths.contains(SYSTEM_LABEL);
407 }
408
409 @Override
410 public boolean havingSystemAuth(User user) throws IOException {
411 if (Superusers.isSuperUser(user)) {
412 return true;
413 }
414 Set<String> auths = new HashSet<String>();
415 auths.addAll(this.getUserAuths(Bytes.toBytes(user.getShortName()), true));
416 auths.addAll(this.getGroupAuths(user.getGroupNames(), true));
417 return auths.contains(SYSTEM_LABEL);
418 }
419
420 @Override
421 public boolean matchVisibility(List<Tag> putTags, Byte putTagsFormat, List<Tag> deleteTags,
422 Byte deleteTagsFormat) throws IOException {
423 assert putTagsFormat == STRING_SERIALIZATION_FORMAT;
424 assert deleteTagsFormat == STRING_SERIALIZATION_FORMAT;
425 return checkForMatchingVisibilityTagsWithSortedOrder(putTags, deleteTags);
426 }
427
428 private static boolean checkForMatchingVisibilityTagsWithSortedOrder(List<Tag> putVisTags,
429 List<Tag> deleteVisTags) {
430 boolean matchFound = false;
431
432
433 if ((deleteVisTags.size()) == putVisTags.size()) {
434 for (Tag tag : deleteVisTags) {
435 matchFound = false;
436 for (Tag givenTag : putVisTags) {
437 if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
438 givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
439 matchFound = true;
440 break;
441 }
442 }
443 if (!matchFound)
444 break;
445 }
446 }
447 return matchFound;
448 }
449
450 @Override
451 public byte[] encodeVisibilityForReplication(final List<Tag> tags, final Byte serializationFormat)
452 throws IOException {
453 if (tags.size() > 0 && (serializationFormat == null
454 || serializationFormat == STRING_SERIALIZATION_FORMAT)) {
455 return createModifiedVisExpression(tags);
456 }
457 return null;
458 }
459
460
461
462
463
464 private byte[] createModifiedVisExpression(final List<Tag> tags)
465 throws IOException {
466 StringBuilder visibilityString = new StringBuilder();
467 for (Tag tag : tags) {
468 if (tag.getType() == TagType.VISIBILITY_TAG_TYPE) {
469 if (visibilityString.length() != 0) {
470 visibilityString.append(VisibilityConstants.CLOSED_PARAN
471 + VisibilityConstants.OR_OPERATOR);
472 }
473 int offset = tag.getTagOffset();
474 int endOffset = offset + tag.getTagLength();
475 boolean expressionStart = true;
476 while (offset < endOffset) {
477 short len = Bytes.toShort(tag.getBuffer(), offset);
478 offset += 2;
479 if (len < 0) {
480 len = (short) (-1 * len);
481 String label = Bytes.toString(tag.getBuffer(), offset, len);
482 if (expressionStart) {
483 visibilityString.append(VisibilityConstants.OPEN_PARAN
484 + VisibilityConstants.NOT_OPERATOR + CellVisibility.quote(label));
485 } else {
486 visibilityString.append(VisibilityConstants.AND_OPERATOR
487 + VisibilityConstants.NOT_OPERATOR + CellVisibility.quote(label));
488 }
489 } else {
490 String label = Bytes.toString(tag.getBuffer(), offset, len);
491 if (expressionStart) {
492 visibilityString.append(VisibilityConstants.OPEN_PARAN + CellVisibility.quote(label));
493 } else {
494 visibilityString.append(VisibilityConstants.AND_OPERATOR
495 + CellVisibility.quote(label));
496 }
497 }
498 expressionStart = false;
499 offset += len;
500 }
501 }
502 }
503 if (visibilityString.length() != 0) {
504 visibilityString.append(VisibilityConstants.CLOSED_PARAN);
505
506 return Bytes.toBytes(visibilityString.toString());
507 }
508 return null;
509 }
510 }