1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security.visibility;
20
21 import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SANITY_CHECK_FAILURE;
22 import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SUCCESS;
23 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
24 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
25
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.hadoop.conf.Configuration;
36 import org.apache.hadoop.hbase.Cell;
37 import org.apache.hadoop.hbase.CellScanner;
38 import org.apache.hadoop.hbase.CellUtil;
39 import org.apache.hadoop.hbase.CoprocessorEnvironment;
40 import org.apache.hadoop.hbase.DoNotRetryIOException;
41 import org.apache.hadoop.hbase.classification.InterfaceAudience;
42 import org.apache.hadoop.hbase.HColumnDescriptor;
43 import org.apache.hadoop.hbase.HConstants;
44 import org.apache.hadoop.hbase.HTableDescriptor;
45 import org.apache.hadoop.hbase.KeyValue;
46 import org.apache.hadoop.hbase.KeyValue.Type;
47 import org.apache.hadoop.hbase.KeyValueUtil;
48 import org.apache.hadoop.hbase.TableName;
49 import org.apache.hadoop.hbase.Tag;
50 import org.apache.hadoop.hbase.TagType;
51 import org.apache.hadoop.hbase.catalog.MetaReader;
52 import org.apache.hadoop.hbase.client.Append;
53 import org.apache.hadoop.hbase.client.Delete;
54 import org.apache.hadoop.hbase.client.Get;
55 import org.apache.hadoop.hbase.client.Increment;
56 import org.apache.hadoop.hbase.client.Mutation;
57 import org.apache.hadoop.hbase.client.Put;
58 import org.apache.hadoop.hbase.client.Result;
59 import org.apache.hadoop.hbase.client.Scan;
60 import org.apache.hadoop.hbase.constraint.ConstraintException;
61 import org.apache.hadoop.hbase.coprocessor.BaseMasterAndRegionObserver;
62 import org.apache.hadoop.hbase.coprocessor.BaseRegionServerObserver;
63 import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
64 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
65 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
66 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
67 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
68 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
69 import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
70 import org.apache.hadoop.hbase.exceptions.DeserializationException;
71 import org.apache.hadoop.hbase.exceptions.FailedSanityCheckException;
72 import org.apache.hadoop.hbase.filter.Filter;
73 import org.apache.hadoop.hbase.filter.FilterBase;
74 import org.apache.hadoop.hbase.filter.FilterList;
75 import org.apache.hadoop.hbase.io.hfile.HFile;
76 import org.apache.hadoop.hbase.ipc.RequestContext;
77 import org.apache.hadoop.hbase.master.MasterServices;
78 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
79 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
80 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos;
81 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsRequest;
82 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
83 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsRequest;
84 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsResponse;
85 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.SetAuthsRequest;
86 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel;
87 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest;
88 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
89 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService;
90 import org.apache.hadoop.hbase.regionserver.BloomType;
91 import org.apache.hadoop.hbase.regionserver.DeleteTracker;
92 import org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy;
93 import org.apache.hadoop.hbase.regionserver.HRegion;
94 import org.apache.hadoop.hbase.regionserver.InternalScanner;
95 import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress;
96 import org.apache.hadoop.hbase.regionserver.OperationStatus;
97 import org.apache.hadoop.hbase.regionserver.RegionScanner;
98 import org.apache.hadoop.hbase.replication.ReplicationEndpoint;
99 import org.apache.hadoop.hbase.security.AccessDeniedException;
100 import org.apache.hadoop.hbase.security.User;
101 import org.apache.hadoop.hbase.security.access.AccessControlLists;
102 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
103 import org.apache.hadoop.hbase.security.access.AccessController;
104 import org.apache.hadoop.hbase.util.ByteStringer;
105 import org.apache.hadoop.hbase.util.Bytes;
106 import org.apache.hadoop.hbase.util.Pair;
107
108 import com.google.common.collect.Lists;
109 import com.google.common.collect.MapMaker;
110 import com.google.protobuf.ByteString;
111 import com.google.protobuf.RpcCallback;
112 import com.google.protobuf.RpcController;
113 import com.google.protobuf.Service;
114
115
116
117
118
119 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
120 public class VisibilityController extends BaseMasterAndRegionObserver implements
121 VisibilityLabelsService.Interface, CoprocessorService {
122
123 private static final Log LOG = LogFactory.getLog(VisibilityController.class);
124
125 private boolean labelsRegion = false;
126
127 private boolean acOn = false;
128 private Configuration conf;
129 private volatile boolean initialized = false;
130 private boolean checkAuths = false;
131
132 private Map<InternalScanner,String> scannerOwners =
133 new MapMaker().weakKeys().makeMap();
134
135 List<String> superUsers;
136 private VisibilityLabelService visibilityLabelService;
137
138
139 private static ArrayList<Byte> RESERVED_VIS_TAG_TYPES = new ArrayList<Byte>();
140 static {
141 RESERVED_VIS_TAG_TYPES.add(TagType.VISIBILITY_TAG_TYPE);
142 RESERVED_VIS_TAG_TYPES.add(TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE);
143 RESERVED_VIS_TAG_TYPES.add(TagType.STRING_VIS_TAG_TYPE);
144 }
145
146 @Override
147 public void start(CoprocessorEnvironment env) throws IOException {
148 this.conf = env.getConfiguration();
149 if (HFile.getFormatVersion(conf) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) {
150 throw new RuntimeException("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS
151 + " is required to persist visibility labels. Consider setting " + HFile.FORMAT_VERSION_KEY
152 + " accordingly.");
153 }
154
155 if (env instanceof RegionServerCoprocessorEnvironment) {
156 throw new RuntimeException("Visibility controller should not be configured as "
157 + "'hbase.coprocessor.regionserver.classes'.");
158 }
159
160 if (!(env instanceof MasterCoprocessorEnvironment)) {
161 visibilityLabelService = VisibilityLabelServiceManager.getInstance()
162 .getVisibilityLabelService(this.conf);
163 }
164 this.superUsers = getSystemAndSuperUsers();
165 }
166
167 @Override
168 public void stop(CoprocessorEnvironment env) throws IOException {
169
170 }
171
172
173
174 @Override
175 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
176
177 MasterServices master = ctx.getEnvironment().getMasterServices();
178 if (!MetaReader.tableExists(master.getCatalogTracker(), LABELS_TABLE_NAME)) {
179 HTableDescriptor labelsTable = new HTableDescriptor(LABELS_TABLE_NAME);
180 HColumnDescriptor labelsColumn = new HColumnDescriptor(LABELS_TABLE_FAMILY);
181 labelsColumn.setBloomFilterType(BloomType.NONE);
182 labelsColumn.setBlockCacheEnabled(false);
183
184 labelsTable.addFamily(labelsColumn);
185
186
187 labelsTable.setValue(HTableDescriptor.SPLIT_POLICY,
188 DisabledRegionSplitPolicy.class.getName());
189 labelsTable.setValue(Bytes.toBytes(HConstants.DISALLOW_WRITES_IN_RECOVERING),
190 Bytes.toBytes(true));
191 master.createTable(labelsTable, null);
192 }
193 }
194
195 @Override
196 public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
197 TableName tableName, HTableDescriptor htd) throws IOException {
198 if (LABELS_TABLE_NAME.equals(tableName)) {
199 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
200 }
201 }
202
203 @Override
204 public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName,
205 HColumnDescriptor column) throws IOException {
206 if (LABELS_TABLE_NAME.equals(tableName)) {
207 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
208 }
209 }
210
211 @Override
212 public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
213 TableName tableName, HColumnDescriptor descriptor) throws IOException {
214 if (LABELS_TABLE_NAME.equals(tableName)) {
215 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
216 }
217 }
218
219 @Override
220 public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
221 TableName tableName, byte[] c) throws IOException {
222 if (LABELS_TABLE_NAME.equals(tableName)) {
223 throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
224 }
225 }
226
227 @Override
228 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
229 throws IOException {
230 if (LABELS_TABLE_NAME.equals(tableName)) {
231 throw new ConstraintException("Cannot disable " + LABELS_TABLE_NAME);
232 }
233 }
234
235
236
237 @Override
238 public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
239
240 if (e.getEnvironment().getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
241 this.labelsRegion = true;
242 this.acOn = CoprocessorHost.getLoadedCoprocessors().contains(AccessController.class.getName());
243
244 if (!e.getEnvironment().getRegion().isRecovering()) {
245 initVisibilityLabelService(e.getEnvironment());
246 }
247 } else {
248 checkAuths = e.getEnvironment().getConfiguration()
249 .getBoolean(VisibilityConstants.CHECK_AUTHS_FOR_MUTATION, false);
250 initVisibilityLabelService(e.getEnvironment());
251 }
252 }
253
254 @Override
255 public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> e) {
256 if (this.labelsRegion) {
257 initVisibilityLabelService(e.getEnvironment());
258 LOG.debug("post labels region log replay");
259 }
260 }
261
262 private void initVisibilityLabelService(RegionCoprocessorEnvironment env) {
263 try {
264 this.visibilityLabelService.init(env);
265 this.initialized = true;
266 } catch (IOException ioe) {
267 LOG.error("Error while initializing VisibilityLabelService..", ioe);
268 throw new RuntimeException(ioe);
269 }
270 }
271
272 @Override
273 public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
274 MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
275 if (c.getEnvironment().getRegion().getRegionInfo().getTable().isSystemTable()) {
276 return;
277 }
278
279 Map<String, List<Tag>> labelCache = new HashMap<String, List<Tag>>();
280 for (int i = 0; i < miniBatchOp.size(); i++) {
281 Mutation m = miniBatchOp.getOperation(i);
282 CellVisibility cellVisibility = null;
283 try {
284 cellVisibility = m.getCellVisibility();
285 } catch (DeserializationException de) {
286 miniBatchOp.setOperationStatus(i,
287 new OperationStatus(SANITY_CHECK_FAILURE, de.getMessage()));
288 continue;
289 }
290 boolean sanityFailure = false;
291 boolean modifiedTagFound = false;
292 Pair<Boolean, Tag> pair = new Pair<Boolean, Tag>(false, null);
293 for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
294 pair = checkForReservedVisibilityTagPresence(cellScanner.current(), pair);
295 if (!pair.getFirst()) {
296 miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE,
297 "Mutation contains cell with reserved type tag"));
298 sanityFailure = true;
299 break;
300 } else {
301
302 Tag tag = pair.getSecond();
303 if (cellVisibility == null && tag != null) {
304
305 cellVisibility = new CellVisibility(Bytes.toString(tag.getBuffer(), tag.getTagOffset(),
306 tag.getTagLength()));
307 modifiedTagFound = true;
308 }
309 }
310 }
311 if (!sanityFailure) {
312 if (cellVisibility != null) {
313 String labelsExp = cellVisibility.getExpression();
314 List<Tag> visibilityTags = labelCache.get(labelsExp);
315 if (visibilityTags == null) {
316
317 boolean authCheck = this.checkAuths && !(isSystemOrSuperUser());
318 try {
319 visibilityTags = this.visibilityLabelService.createVisibilityExpTags(labelsExp, true,
320 authCheck);
321 } catch (InvalidLabelException e) {
322 miniBatchOp.setOperationStatus(i,
323 new OperationStatus(SANITY_CHECK_FAILURE, e.getMessage()));
324 }
325 if (visibilityTags != null) {
326 labelCache.put(labelsExp, visibilityTags);
327 }
328 }
329 if (visibilityTags != null) {
330 List<Cell> updatedCells = new ArrayList<Cell>();
331 for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
332 Cell cell = cellScanner.current();
333 List<Tag> tags = Tag.asList(cell.getTagsArray(), cell.getTagsOffset(),
334 cell.getTagsLengthUnsigned());
335 if (modifiedTagFound) {
336
337 removeReplicationVisibilityTag(tags);
338 }
339 tags.addAll(visibilityTags);
340 Cell updatedCell = new KeyValue(cell.getRowArray(), cell.getRowOffset(),
341 cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(),
342 cell.getFamilyLength(), cell.getQualifierArray(), cell.getQualifierOffset(),
343 cell.getQualifierLength(), cell.getTimestamp(), Type.codeToType(cell
344 .getTypeByte()), cell.getValueArray(), cell.getValueOffset(),
345 cell.getValueLength(), tags);
346 updatedCells.add(updatedCell);
347 }
348 m.getFamilyCellMap().clear();
349
350 for (Cell cell : updatedCells) {
351 if (m instanceof Put) {
352 Put p = (Put) m;
353 p.add(cell);
354 } else if (m instanceof Delete) {
355
356 Delete d = (Delete) m;
357 d.addDeleteMarker(cell);
358 }
359 }
360 }
361 }
362 }
363 }
364 }
365
366 @Override
367 public void prePrepareTimeStampForDeleteVersion(
368 ObserverContext<RegionCoprocessorEnvironment> ctx, Mutation delete, Cell cell,
369 byte[] byteNow, Get get) throws IOException {
370 KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
371 CellVisibility cellVisibility = null;
372 try {
373 cellVisibility = delete.getCellVisibility();
374 } catch (DeserializationException de) {
375 throw new IOException("Invalid cell visibility specified " + delete, de);
376 }
377
378
379 List<Tag> visibilityTags = new ArrayList<Tag>();
380 if (cellVisibility != null) {
381 String labelsExp = cellVisibility.getExpression();
382 try {
383 visibilityTags = this.visibilityLabelService.createVisibilityExpTags(labelsExp, false,
384 false);
385 } catch (InvalidLabelException e) {
386 throw new IOException("Invalid cell visibility specified " + labelsExp, e);
387 }
388 }
389 get.setFilter(new DeleteVersionVisibilityExpressionFilter(visibilityTags,
390 VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT));
391 List<Cell> result = ctx.getEnvironment().getRegion().get(get, false);
392
393 if (result.size() < get.getMaxVersions()) {
394
395 kv.updateLatestStamp(Bytes.toBytes(Long.MIN_VALUE));
396 return;
397 }
398 if (result.size() > get.getMaxVersions()) {
399 throw new RuntimeException("Unexpected size: " + result.size()
400 + ". Results more than the max versions obtained.");
401 }
402 KeyValue getkv = KeyValueUtil.ensureKeyValue(result.get(get.getMaxVersions() - 1));
403 Bytes.putBytes(kv.getBuffer(), kv.getTimestampOffset(), getkv.getBuffer(),
404 getkv.getTimestampOffset(), Bytes.SIZEOF_LONG);
405
406
407
408
409
410 ctx.bypass();
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425 private Pair<Boolean, Tag> checkForReservedVisibilityTagPresence(Cell cell,
426 Pair<Boolean, Tag> pair) throws IOException {
427 if (pair == null) {
428 pair = new Pair<Boolean, Tag>(false, null);
429 } else {
430 pair.setFirst(false);
431 pair.setSecond(null);
432 }
433
434
435
436 if (isSystemOrSuperUser()) {
437
438
439
440 Tag modifiedTag = null;
441 if (cell.getTagsLength() > 0) {
442 Iterator<Tag> tagsIterator = CellUtil.tagsIterator(cell.getTagsArray(),
443 cell.getTagsOffset(), cell.getTagsLength());
444 while (tagsIterator.hasNext()) {
445 Tag tag = tagsIterator.next();
446 if (tag.getType() == TagType.STRING_VIS_TAG_TYPE) {
447 modifiedTag = tag;
448 break;
449 }
450 }
451 }
452 pair.setFirst(true);
453 pair.setSecond(modifiedTag);
454 return pair;
455 }
456 if (cell.getTagsLength() > 0) {
457 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
458 cell.getTagsLength());
459 while (tagsItr.hasNext()) {
460 if (RESERVED_VIS_TAG_TYPES.contains(tagsItr.next().getType())) {
461 return pair;
462 }
463 }
464 }
465 pair.setFirst(true);
466 return pair;
467 }
468
469
470
471
472
473
474
475
476
477
478
479
480
481 private boolean checkForReservedVisibilityTagPresence(Cell cell) throws IOException {
482
483
484
485
486
487 if (isSystemOrSuperUser()) {
488 return true;
489 }
490 if (cell.getTagsLengthUnsigned() > 0) {
491 Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
492 cell.getTagsLengthUnsigned());
493 while (tagsItr.hasNext()) {
494 if (RESERVED_VIS_TAG_TYPES.contains(tagsItr.next().getType())) {
495 return false;
496 }
497 }
498 }
499 return true;
500 }
501
502 private void removeReplicationVisibilityTag(List<Tag> tags) throws IOException {
503 Iterator<Tag> iterator = tags.iterator();
504 while (iterator.hasNext()) {
505 Tag tag = iterator.next();
506 if (tag.getType() == TagType.STRING_VIS_TAG_TYPE) {
507 iterator.remove();
508 break;
509 }
510 }
511 }
512
513 @Override
514 public RegionScanner preScannerOpen(ObserverContext<RegionCoprocessorEnvironment> e, Scan scan,
515 RegionScanner s) throws IOException {
516 if (!initialized) {
517 throw new VisibilityControllerNotReadyException("VisibilityController not yet initialized!");
518 }
519 HRegion region = e.getEnvironment().getRegion();
520 Authorizations authorizations = null;
521 try {
522 authorizations = scan.getAuthorizations();
523 } catch (DeserializationException de) {
524 throw new IOException(de);
525 }
526 if (authorizations == null) {
527
528
529
530 TableName table = region.getRegionInfo().getTable();
531 if (table.isSystemTable() && !table.equals(LABELS_TABLE_NAME)) {
532 return s;
533 }
534 }
535
536 Filter visibilityLabelFilter = VisibilityUtils.createVisibilityLabelFilter(region,
537 authorizations);
538 if (visibilityLabelFilter != null) {
539 Filter filter = scan.getFilter();
540 if (filter != null) {
541 scan.setFilter(new FilterList(filter, visibilityLabelFilter));
542 } else {
543 scan.setFilter(visibilityLabelFilter);
544 }
545 }
546 return s;
547 }
548
549 @Override
550 public DeleteTracker postInstantiateDeleteTracker(
551 ObserverContext<RegionCoprocessorEnvironment> ctx, DeleteTracker delTracker)
552 throws IOException {
553 HRegion region = ctx.getEnvironment().getRegion();
554 TableName table = region.getRegionInfo().getTable();
555 if (table.isSystemTable()) {
556 return delTracker;
557 }
558
559
560
561
562
563 return new VisibilityScanDeleteTracker();
564 }
565
566 @Override
567 public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
568 final Scan scan, final RegionScanner s) throws IOException {
569 User user = VisibilityUtils.getActiveUser();
570 if (user != null && user.getShortName() != null) {
571 scannerOwners.put(s, user.getShortName());
572 }
573 return s;
574 }
575
576 @Override
577 public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
578 final InternalScanner s, final List<Result> result, final int limit, final boolean hasNext)
579 throws IOException {
580 requireScannerOwner(s);
581 return hasNext;
582 }
583
584 @Override
585 public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
586 final InternalScanner s) throws IOException {
587 requireScannerOwner(s);
588 }
589
590 @Override
591 public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
592 final InternalScanner s) throws IOException {
593
594 scannerOwners.remove(s);
595 }
596
597
598
599
600
601 private void requireScannerOwner(InternalScanner s) throws AccessDeniedException {
602 if (RequestContext.isInRequestContext()) {
603 String requestUName = RequestContext.getRequestUserName();
604 String owner = scannerOwners.get(s);
605 if (owner != null && !owner.equals(requestUName)) {
606 throw new AccessDeniedException("User '" + requestUName + "' is not the scanner owner!");
607 }
608 }
609 }
610
611 @Override
612 public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results)
613 throws IOException {
614 if (!initialized) {
615 throw new VisibilityControllerNotReadyException("VisibilityController not yet initialized!");
616 }
617 HRegion region = e.getEnvironment().getRegion();
618 Authorizations authorizations = null;
619 try {
620 authorizations = get.getAuthorizations();
621 } catch (DeserializationException de) {
622 throw new IOException(de);
623 }
624 if (authorizations == null) {
625
626
627
628 TableName table = region.getRegionInfo().getTable();
629 if (table.isSystemTable() && !table.equals(LABELS_TABLE_NAME)) {
630 return;
631 }
632 }
633 Filter visibilityLabelFilter = VisibilityUtils.createVisibilityLabelFilter(e.getEnvironment()
634 .getRegion(), authorizations);
635 if (visibilityLabelFilter != null) {
636 Filter filter = get.getFilter();
637 if (filter != null) {
638 get.setFilter(new FilterList(filter, visibilityLabelFilter));
639 } else {
640 get.setFilter(visibilityLabelFilter);
641 }
642 }
643 }
644
645 private List<String> getSystemAndSuperUsers() throws IOException {
646 User user = User.getCurrent();
647 if (user == null) {
648 throw new IOException("Unable to obtain the current user, "
649 + "authorization checks for internal operations will not work correctly!");
650 }
651 if (LOG.isTraceEnabled()) {
652 LOG.trace("Current user name is "+user.getShortName());
653 }
654 String currentUser = user.getShortName();
655 List<String> superUsers = Lists.asList(currentUser,
656 this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
657 return superUsers;
658 }
659
660 private boolean isSystemOrSuperUser() throws IOException {
661 User activeUser = VisibilityUtils.getActiveUser();
662 return this.superUsers.contains(activeUser.getShortName());
663 }
664
665 @Override
666 public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> e, Append append)
667 throws IOException {
668 for (CellScanner cellScanner = append.cellScanner(); cellScanner.advance();) {
669 if (!checkForReservedVisibilityTagPresence(cellScanner.current())) {
670 throw new FailedSanityCheckException("Append contains cell with reserved type tag");
671 }
672 }
673 return null;
674 }
675
676 @Override
677 public Result preIncrement(ObserverContext<RegionCoprocessorEnvironment> e, Increment increment)
678 throws IOException {
679 for (CellScanner cellScanner = increment.cellScanner(); cellScanner.advance();) {
680 if (!checkForReservedVisibilityTagPresence(cellScanner.current())) {
681 throw new FailedSanityCheckException("Increment contains cell with reserved type tag");
682 }
683 }
684 return null;
685 }
686
687 @Override
688 public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
689 MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException {
690 List<Tag> tags = Lists.newArrayList();
691 CellVisibility cellVisibility = null;
692 try {
693 cellVisibility = mutation.getCellVisibility();
694 } catch (DeserializationException e) {
695 throw new IOException(e);
696 }
697 if (cellVisibility == null) {
698 return newCell;
699 }
700
701
702 boolean authCheck = this.checkAuths && !(isSystemOrSuperUser());
703 tags.addAll(this.visibilityLabelService.createVisibilityExpTags(cellVisibility.getExpression(),
704 true, authCheck));
705
706 if (newCell.getTagsLengthUnsigned() > 0) {
707
708 Iterator<Tag> tagsItr = CellUtil.tagsIterator(newCell.getTagsArray(),
709 newCell.getTagsOffset(), newCell.getTagsLengthUnsigned());
710 while (tagsItr.hasNext()) {
711 Tag tag = tagsItr.next();
712 if (tag.getType() != TagType.VISIBILITY_TAG_TYPE
713 && tag.getType() != TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE) {
714 tags.add(tag);
715 }
716 }
717 }
718
719
720
721 KeyValue rewriteKv = new KeyValue(newCell.getRowArray(), newCell.getRowOffset(),
722 newCell.getRowLength(), newCell.getFamilyArray(), newCell.getFamilyOffset(),
723 newCell.getFamilyLength(), newCell.getQualifierArray(), newCell.getQualifierOffset(),
724 newCell.getQualifierLength(), newCell.getTimestamp(), KeyValue.Type.codeToType(newCell
725 .getTypeByte()), newCell.getValueArray(), newCell.getValueOffset(),
726 newCell.getValueLength(), tags);
727
728 rewriteKv.setMvccVersion(newCell.getMvccVersion());
729 return rewriteKv;
730 }
731
732 @Override
733 public Service getService() {
734 return VisibilityLabelsProtos.VisibilityLabelsService.newReflectiveService(this);
735 }
736
737
738 @Override
739 public synchronized void addLabels(RpcController controller, VisibilityLabelsRequest request,
740 RpcCallback<VisibilityLabelsResponse> done) {
741 VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
742 List<VisibilityLabel> visLabels = request.getVisLabelList();
743 if (!initialized) {
744 setExceptionResults(visLabels.size(),
745 new VisibilityControllerNotReadyException("VisibilityController not yet initialized!"),
746 response);
747 } else {
748 try {
749 checkCallingUserAuth();
750 List<byte[]> labels = new ArrayList<byte[]>(visLabels.size());
751 RegionActionResult successResult = RegionActionResult.newBuilder().build();
752 for (VisibilityLabel visLabel : visLabels) {
753 byte[] label = visLabel.getLabel().toByteArray();
754 labels.add(label);
755 response.addResult(successResult);
756
757
758 }
759 if (!labels.isEmpty()) {
760 OperationStatus[] opStatus = this.visibilityLabelService.addLabels(labels);
761 int i = 0;
762 for (OperationStatus status : opStatus) {
763 while (response.getResult(i) != successResult)
764 i++;
765 if (status.getOperationStatusCode() != SUCCESS) {
766 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
767 failureResultBuilder.setException(ResponseConverter
768 .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
769 response.setResult(i, failureResultBuilder.build());
770 }
771 i++;
772 }
773 }
774 } catch (IOException e) {
775 LOG.error(e);
776 setExceptionResults(visLabels.size(), e, response);
777 }
778 }
779 done.run(response.build());
780 }
781
782 private void setExceptionResults(int size, IOException e,
783 VisibilityLabelsResponse.Builder response) {
784 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
785 failureResultBuilder.setException(ResponseConverter.buildException(e));
786 RegionActionResult failureResult = failureResultBuilder.build();
787 for (int i = 0; i < size; i++) {
788 response.addResult(i, failureResult);
789 }
790 }
791
792 @Override
793 public synchronized void setAuths(RpcController controller, SetAuthsRequest request,
794 RpcCallback<VisibilityLabelsResponse> done) {
795 VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
796 List<ByteString> auths = request.getAuthList();
797 if (!initialized) {
798 setExceptionResults(auths.size(),
799 new VisibilityControllerNotReadyException("VisibilityController not yet initialized!"),
800 response);
801 } else {
802 try {
803 checkCallingUserAuth();
804 List<byte[]> labelAuths = new ArrayList<byte[]>(auths.size());
805 for (ByteString authBS : auths) {
806 labelAuths.add(authBS.toByteArray());
807 }
808 OperationStatus[] opStatus = this.visibilityLabelService.setAuths(request.getUser()
809 .toByteArray(), labelAuths);
810 RegionActionResult successResult = RegionActionResult.newBuilder().build();
811 for (OperationStatus status : opStatus) {
812 if (status.getOperationStatusCode() == SUCCESS) {
813 response.addResult(successResult);
814 } else {
815 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
816 failureResultBuilder.setException(ResponseConverter
817 .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
818 response.addResult(failureResultBuilder.build());
819 }
820 }
821 } catch (IOException e) {
822 LOG.error(e);
823 setExceptionResults(auths.size(), e, response);
824 }
825 }
826 done.run(response.build());
827 }
828
829 @Override
830 public synchronized void getAuths(RpcController controller, GetAuthsRequest request,
831 RpcCallback<GetAuthsResponse> done) {
832 GetAuthsResponse.Builder response = GetAuthsResponse.newBuilder();
833 if (!initialized) {
834 controller.setFailed("VisibilityController not yet initialized");
835 } else {
836 byte[] user = request.getUser().toByteArray();
837 List<String> labels = null;
838 try {
839
840
841 if (this.acOn && !isSystemOrSuperUser()) {
842 User requestingUser = VisibilityUtils.getActiveUser();
843 throw new AccessDeniedException("User '"
844 + (requestingUser != null ? requestingUser.getShortName() : "null")
845 + "' is not authorized to perform this action.");
846 }
847 labels = this.visibilityLabelService.getAuths(user, false);
848 } catch (IOException e) {
849 ResponseConverter.setControllerException(controller, e);
850 }
851 response.setUser(request.getUser());
852 if (labels != null) {
853 for (String label : labels) {
854 response.addAuth(ByteStringer.wrap(Bytes.toBytes(label)));
855 }
856 }
857 }
858 done.run(response.build());
859 }
860
861 @Override
862 public synchronized void clearAuths(RpcController controller, SetAuthsRequest request,
863 RpcCallback<VisibilityLabelsResponse> done) {
864 VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
865 List<ByteString> auths = request.getAuthList();
866 if (!initialized) {
867 setExceptionResults(auths.size(), new CoprocessorException(
868 "VisibilityController not yet initialized"), response);
869 } else {
870 try {
871
872 if (this.acOn && !isSystemOrSuperUser()) {
873 User user = VisibilityUtils.getActiveUser();
874 throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null")
875 + " is not authorized to perform this action.");
876 }
877 checkCallingUserAuth();
878
879 List<byte[]> labelAuths = new ArrayList<byte[]>(auths.size());
880 for (ByteString authBS : auths) {
881 labelAuths.add(authBS.toByteArray());
882 }
883 OperationStatus[] opStatus = this.visibilityLabelService.clearAuths(request.getUser()
884 .toByteArray(), labelAuths);
885 RegionActionResult successResult = RegionActionResult.newBuilder().build();
886 for (OperationStatus status : opStatus) {
887 if (status.getOperationStatusCode() == SUCCESS) {
888 response.addResult(successResult);
889 } else {
890 RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
891 failureResultBuilder.setException(ResponseConverter
892 .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
893 response.addResult(failureResultBuilder.build());
894 }
895 }
896 } catch (IOException e) {
897 LOG.error(e);
898 setExceptionResults(auths.size(), e, response);
899 }
900 }
901 done.run(response.build());
902 }
903
904 @Override
905 public synchronized void listLabels(RpcController controller, ListLabelsRequest request,
906 RpcCallback<ListLabelsResponse> done) {
907 ListLabelsResponse.Builder response = ListLabelsResponse.newBuilder();
908 if (!initialized) {
909 controller.setFailed("VisibilityController not yet initialized");
910 } else {
911 List<String> labels = null;
912 try {
913
914
915 if (this.acOn && !isSystemOrSuperUser()) {
916 User requestingUser = VisibilityUtils.getActiveUser();
917 throw new AccessDeniedException("User '"
918 + (requestingUser != null ? requestingUser.getShortName() : "null")
919 + "' is not authorized to perform this action.");
920 }
921 String regex = request.hasRegex() ? request.getRegex() : null;
922 labels = this.visibilityLabelService.listLabels(regex);
923 } catch (IOException e) {
924 ResponseConverter.setControllerException(controller, e);
925 }
926 if (labels != null && !labels.isEmpty()) {
927 for (String label : labels) {
928 response.addLabel(ByteStringer.wrap(Bytes.toBytes(label)));
929 }
930 }
931 }
932 done.run(response.build());
933 }
934
935 private void checkCallingUserAuth() throws IOException {
936 if (!this.acOn) {
937 User user = VisibilityUtils.getActiveUser();
938 if (user == null) {
939 throw new IOException("Unable to retrieve calling user");
940 }
941 if (!(this.visibilityLabelService.havingSystemAuth(Bytes.toBytes(user.getShortName())))) {
942 throw new AccessDeniedException("User '" + user.getShortName()
943 + "' is not authorized to perform this action.");
944 }
945 }
946 }
947
948 private static class DeleteVersionVisibilityExpressionFilter extends FilterBase {
949 private List<Tag> deleteCellVisTags;
950 private Byte deleteCellVisTagsFormat;
951
952 public DeleteVersionVisibilityExpressionFilter(List<Tag> deleteCellVisTags,
953 Byte deleteCellVisTagsFormat) {
954 this.deleteCellVisTags = deleteCellVisTags;
955 this.deleteCellVisTagsFormat = deleteCellVisTagsFormat;
956 }
957
958 @Override
959 public ReturnCode filterKeyValue(Cell cell) throws IOException {
960 List<Tag> putVisTags = new ArrayList<Tag>();
961 Byte putCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(cell, putVisTags);
962 boolean matchFound = VisibilityLabelServiceManager
963 .getInstance().getVisibilityLabelService()
964 .matchVisibility(putVisTags, putCellVisTagsFormat, deleteCellVisTags,
965 deleteCellVisTagsFormat);
966 return matchFound ? ReturnCode.INCLUDE : ReturnCode.SKIP;
967 }
968 }
969
970 @Override
971 public void preTruncateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
972 TableName tableName) throws IOException {
973 }
974
975 @Override
976 public void postTruncateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
977 TableName tableName) throws IOException {
978 }
979
980 @Override
981 public void preTruncateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
982 TableName tableName) throws IOException {
983 }
984
985 @Override
986 public void postTruncateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
987 TableName tableName) throws IOException {
988 }
989
990
991
992
993
994
995
996
997 public static class VisibilityReplication extends BaseRegionServerObserver {
998 private Configuration conf;
999 private VisibilityLabelService visibilityLabelService;
1000
1001 @Override
1002 public void start(CoprocessorEnvironment env) throws IOException {
1003 this.conf = env.getConfiguration();
1004 visibilityLabelService = VisibilityLabelServiceManager.getInstance()
1005 .getVisibilityLabelService(this.conf);
1006 }
1007
1008 @Override
1009 public void stop(CoprocessorEnvironment env) throws IOException {
1010 }
1011
1012 @Override
1013 public ReplicationEndpoint postCreateReplicationEndPoint(
1014 ObserverContext<RegionServerCoprocessorEnvironment> ctx, ReplicationEndpoint endpoint) {
1015 return new VisibilityReplicationEndpoint(endpoint, visibilityLabelService);
1016 }
1017 }
1018 }