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