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 static org.apache.hadoop.hbase.TagType.VISIBILITY_TAG_TYPE; 021import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY; 022import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME; 023import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABEL_QUALIFIER; 024import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT; 025import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL; 026 027import java.io.ByteArrayOutputStream; 028import java.io.DataOutputStream; 029import java.io.IOException; 030import java.util.ArrayList; 031import java.util.Arrays; 032import java.util.BitSet; 033import java.util.Collections; 034import java.util.HashMap; 035import java.util.HashSet; 036import java.util.Iterator; 037import java.util.List; 038import java.util.Map; 039import java.util.Set; 040import java.util.concurrent.atomic.AtomicInteger; 041import java.util.regex.Pattern; 042 043import org.apache.hadoop.conf.Configuration; 044import org.apache.hadoop.hbase.ArrayBackedTag; 045import org.apache.hadoop.hbase.AuthUtil; 046import org.apache.hadoop.hbase.Cell; 047import org.apache.hadoop.hbase.Cell.Type; 048import org.apache.hadoop.hbase.CellBuilderFactory; 049import org.apache.hadoop.hbase.CellBuilderType; 050import org.apache.hadoop.hbase.CellUtil; 051import org.apache.hadoop.hbase.ExtendedCellBuilder; 052import org.apache.hadoop.hbase.ExtendedCellBuilderFactory; 053import org.apache.hadoop.hbase.HConstants.OperationStatusCode; 054import org.apache.hadoop.hbase.PrivateCellUtil; 055import org.apache.hadoop.hbase.Tag; 056import org.apache.hadoop.hbase.TagType; 057import org.apache.hadoop.hbase.TagUtil; 058import org.apache.hadoop.hbase.client.Delete; 059import org.apache.hadoop.hbase.client.Mutation; 060import org.apache.hadoop.hbase.client.Put; 061import org.apache.hadoop.hbase.client.Scan; 062import org.apache.hadoop.hbase.coprocessor.HasRegionServerServices; 063import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 064import org.apache.hadoop.hbase.filter.Filter; 065import org.apache.hadoop.hbase.io.util.StreamUtils; 066import org.apache.hadoop.hbase.regionserver.OperationStatus; 067import org.apache.hadoop.hbase.regionserver.Region; 068import org.apache.hadoop.hbase.regionserver.RegionScanner; 069import org.apache.hadoop.hbase.security.Superusers; 070import org.apache.hadoop.hbase.security.User; 071import org.apache.hadoop.hbase.util.Bytes; 072import org.apache.hadoop.hbase.util.Pair; 073import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 074import org.apache.yetus.audience.InterfaceAudience; 075import org.slf4j.Logger; 076import org.slf4j.LoggerFactory; 077 078@InterfaceAudience.Private 079public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService { 080 private static final Logger LOG = 081 LoggerFactory.getLogger(DefaultVisibilityLabelServiceImpl.class); 082 083 // "system" label is having an ordinal value 1. 084 private static final int SYSTEM_LABEL_ORDINAL = 1; 085 private static final Tag[] LABELS_TABLE_TAGS = new Tag[1]; 086 private static final byte[] DUMMY_VALUE = new byte[0]; 087 088 private AtomicInteger ordinalCounter = new AtomicInteger(-1); 089 private Configuration conf; 090 private Region labelsRegion; 091 private VisibilityLabelsCache labelsCache; 092 private List<ScanLabelGenerator> scanLabelGenerators; 093 094 static { 095 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 096 DataOutputStream dos = new DataOutputStream(baos); 097 try { 098 StreamUtils.writeRawVInt32(dos, SYSTEM_LABEL_ORDINAL); 099 } catch (IOException e) { 100 // We write to a byte array. No Exception can happen. 101 } 102 LABELS_TABLE_TAGS[0] = new ArrayBackedTag(VISIBILITY_TAG_TYPE, baos.toByteArray()); 103 } 104 105 public DefaultVisibilityLabelServiceImpl() { 106 107 } 108 109 @Override 110 public void setConf(Configuration conf) { 111 this.conf = conf; 112 } 113 114 @Override 115 public Configuration getConf() { 116 return this.conf; 117 } 118 119 @Override 120 public void init(RegionCoprocessorEnvironment e) throws IOException { 121 /* So, presumption that the RegionCE has a ZK Connection is too much. Why would a RCE have 122 * a ZK instance? This is cheating presuming we have access to the RS ZKW. TODO: Fix. 123 * 124 * And what is going on here? This ain't even a Coprocessor? And its being passed a CP Env? 125 */ 126 // This is a CoreCoprocessor. On creation, we should have gotten an environment that 127 // implements HasRegionServerServices so we can get at RSS. FIX!!!! Integrate this CP as 128 // native service. 129 ZKWatcher zk = ((HasRegionServerServices)e).getRegionServerServices().getZooKeeper(); 130 try { 131 labelsCache = VisibilityLabelsCache.createAndGet(zk, this.conf); 132 } catch (IOException ioe) { 133 LOG.error("Error creating VisibilityLabelsCache", ioe); 134 throw ioe; 135 } 136 this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf); 137 if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) { 138 this.labelsRegion = e.getRegion(); 139 Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths = 140 extractLabelsAndAuths(getExistingLabelsWithAuths()); 141 Map<String, Integer> labels = labelsAndUserAuths.getFirst(); 142 Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond(); 143 // Add the "system" label if it is not added into the system yet 144 addSystemLabel(this.labelsRegion, labels, userAuths); 145 int ordinal = SYSTEM_LABEL_ORDINAL; // Ordinal 1 is reserved for "system" label. 146 for (Integer i : labels.values()) { 147 if (i > ordinal) { 148 ordinal = i; 149 } 150 } 151 this.ordinalCounter.set(ordinal + 1); 152 if (labels.size() > 0) { 153 // If there is no data need not write to zk 154 byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(labels); 155 this.labelsCache.writeToZookeeper(serialized, true); 156 this.labelsCache.refreshLabelsCache(serialized); 157 } 158 if (userAuths.size() > 0) { 159 byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths); 160 this.labelsCache.writeToZookeeper(serialized, false); 161 this.labelsCache.refreshUserAuthsCache(serialized); 162 } 163 } 164 } 165 166 protected List<List<Cell>> getExistingLabelsWithAuths() throws IOException { 167 Scan scan = new Scan(); 168 RegionScanner scanner = labelsRegion.getScanner(scan); 169 List<List<Cell>> existingLabels = new ArrayList<>(); 170 try { 171 while (true) { 172 List<Cell> cells = new ArrayList<>(); 173 scanner.next(cells); 174 if (cells.isEmpty()) { 175 break; 176 } 177 existingLabels.add(cells); 178 } 179 } finally { 180 scanner.close(); 181 } 182 return existingLabels; 183 } 184 185 protected Pair<Map<String, Integer>, Map<String, List<Integer>>> extractLabelsAndAuths( 186 List<List<Cell>> labelDetails) { 187 Map<String, Integer> labels = new HashMap<>(); 188 Map<String, List<Integer>> userAuths = new HashMap<>(); 189 for (List<Cell> cells : labelDetails) { 190 for (Cell cell : cells) { 191 if (CellUtil.matchingQualifier(cell, LABEL_QUALIFIER)) { 192 labels.put( 193 Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()), 194 PrivateCellUtil.getRowAsInt(cell)); 195 } else { 196 // These are user cells who has authorization for this label 197 String user = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), 198 cell.getQualifierLength()); 199 List<Integer> auths = userAuths.get(user); 200 if (auths == null) { 201 auths = new ArrayList<>(); 202 userAuths.put(user, auths); 203 } 204 auths.add(PrivateCellUtil.getRowAsInt(cell)); 205 } 206 } 207 } 208 return new Pair<>(labels, userAuths); 209 } 210 211 protected void addSystemLabel(Region region, Map<String, Integer> labels, 212 Map<String, List<Integer>> userAuths) throws IOException { 213 if (!labels.containsKey(SYSTEM_LABEL)) { 214 byte[] row = Bytes.toBytes(SYSTEM_LABEL_ORDINAL); 215 Put p = new Put(row); 216 p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 217 .setRow(row) 218 .setFamily(LABELS_TABLE_FAMILY) 219 .setQualifier(LABEL_QUALIFIER) 220 .setTimestamp(p.getTimestamp()) 221 .setType(Type.Put) 222 .setValue(Bytes.toBytes(SYSTEM_LABEL)) 223 .build()); 224 region.put(p); 225 labels.put(SYSTEM_LABEL, SYSTEM_LABEL_ORDINAL); 226 } 227 } 228 229 @Override 230 public OperationStatus[] addLabels(List<byte[]> labels) throws IOException { 231 assert labelsRegion != null; 232 OperationStatus[] finalOpStatus = new OperationStatus[labels.size()]; 233 List<Mutation> puts = new ArrayList<>(labels.size()); 234 int i = 0; 235 ExtendedCellBuilder builder = ExtendedCellBuilderFactory.create(CellBuilderType.SHALLOW_COPY); 236 for (byte[] label : labels) { 237 String labelStr = Bytes.toString(label); 238 if (this.labelsCache.getLabelOrdinal(labelStr) > 0) { 239 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE, 240 new LabelAlreadyExistsException("Label '" + labelStr + "' already exists")); 241 } else { 242 byte[] row = Bytes.toBytes(ordinalCounter.get()); 243 Put p = new Put(row); 244 p.add(builder.clear() 245 .setRow(row) 246 .setFamily(LABELS_TABLE_FAMILY) 247 .setQualifier(LABEL_QUALIFIER) 248 .setTimestamp(p.getTimestamp()) 249 .setType(Type.Put) 250 .setValue(label) 251 .setTags(TagUtil.fromList(Arrays.asList(LABELS_TABLE_TAGS))) 252 .build()); 253 if (LOG.isDebugEnabled()) { 254 LOG.debug("Adding the label " + labelStr); 255 } 256 puts.add(p); 257 ordinalCounter.incrementAndGet(); 258 } 259 i++; 260 } 261 if (mutateLabelsRegion(puts, finalOpStatus)) { 262 updateZk(true); 263 } 264 return finalOpStatus; 265 } 266 267 @Override 268 public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException { 269 assert labelsRegion != null; 270 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()]; 271 List<Mutation> puts = new ArrayList<>(authLabels.size()); 272 int i = 0; 273 ExtendedCellBuilder builder = ExtendedCellBuilderFactory.create(CellBuilderType.SHALLOW_COPY); 274 for (byte[] auth : authLabels) { 275 String authStr = Bytes.toString(auth); 276 int labelOrdinal = this.labelsCache.getLabelOrdinal(authStr); 277 if (labelOrdinal == 0) { 278 // This label is not yet added. 1st this should be added to the system 279 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE, 280 new InvalidLabelException("Label '" + authStr + "' doesn't exists")); 281 } else { 282 byte[] row = Bytes.toBytes(labelOrdinal); 283 Put p = new Put(row); 284 p.add(builder.clear() 285 .setRow(row) 286 .setFamily(LABELS_TABLE_FAMILY) 287 .setQualifier(user) 288 .setTimestamp(p.getTimestamp()) 289 .setType(Cell.Type.Put) 290 .setValue(DUMMY_VALUE) 291 .setTags(TagUtil.fromList(Arrays.asList(LABELS_TABLE_TAGS))) 292 .build()); 293 puts.add(p); 294 } 295 i++; 296 } 297 if (mutateLabelsRegion(puts, finalOpStatus)) { 298 updateZk(false); 299 } 300 return finalOpStatus; 301 } 302 303 @Override 304 public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException { 305 assert labelsRegion != null; 306 OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()]; 307 List<String> currentAuths; 308 if (AuthUtil.isGroupPrincipal(Bytes.toString(user))) { 309 String group = AuthUtil.getGroupName(Bytes.toString(user)); 310 currentAuths = this.getGroupAuths(new String[]{group}, true); 311 } 312 else { 313 currentAuths = this.getUserAuths(user, true); 314 } 315 List<Mutation> deletes = new ArrayList<>(authLabels.size()); 316 int i = 0; 317 for (byte[] authLabel : authLabels) { 318 String authLabelStr = Bytes.toString(authLabel); 319 if (currentAuths.contains(authLabelStr)) { 320 int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabelStr); 321 assert labelOrdinal > 0; 322 Delete d = new Delete(Bytes.toBytes(labelOrdinal)); 323 d.addColumns(LABELS_TABLE_FAMILY, user); 324 deletes.add(d); 325 } else { 326 // This label is not set for the user. 327 finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE, 328 new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user " 329 + Bytes.toString(user))); 330 } 331 i++; 332 } 333 if (mutateLabelsRegion(deletes, finalOpStatus)) { 334 updateZk(false); 335 } 336 return finalOpStatus; 337 } 338 339 /** 340 * Adds the mutations to labels region and set the results to the finalOpStatus. finalOpStatus 341 * might have some entries in it where the OpStatus is FAILURE. We will leave those and set in 342 * others in the order. 343 * @param mutations 344 * @param finalOpStatus 345 * @return whether we need a ZK update or not. 346 */ 347 private boolean mutateLabelsRegion(List<Mutation> mutations, OperationStatus[] finalOpStatus) 348 throws IOException { 349 OperationStatus[] opStatus = this.labelsRegion.batchMutate(mutations 350 .toArray(new Mutation[mutations.size()])); 351 int i = 0; 352 boolean updateZk = false; 353 for (OperationStatus status : opStatus) { 354 // Update the zk when atleast one of the mutation was added successfully. 355 updateZk = updateZk || (status.getOperationStatusCode() == OperationStatusCode.SUCCESS); 356 for (; i < finalOpStatus.length; i++) { 357 if (finalOpStatus[i] == null) { 358 finalOpStatus[i] = status; 359 break; 360 } 361 } 362 } 363 return updateZk; 364 } 365 366 @Override 367 public List<String> getUserAuths(byte[] user, boolean systemCall) 368 throws IOException { 369 assert (labelsRegion != null || systemCall); 370 if (systemCall || labelsRegion == null) { 371 return this.labelsCache.getUserAuths(Bytes.toString(user)); 372 } 373 Scan s = new Scan(); 374 if (user != null && user.length > 0) { 375 s.addColumn(LABELS_TABLE_FAMILY, user); 376 } 377 Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion, 378 new Authorizations(SYSTEM_LABEL)); 379 s.setFilter(filter); 380 ArrayList<String> auths = new ArrayList<>(); 381 RegionScanner scanner = this.labelsRegion.getScanner(s); 382 try { 383 List<Cell> results = new ArrayList<>(1); 384 while (true) { 385 scanner.next(results); 386 if (results.isEmpty()) break; 387 Cell cell = results.get(0); 388 int ordinal = PrivateCellUtil.getRowAsInt(cell); 389 String label = this.labelsCache.getLabel(ordinal); 390 if (label != null) { 391 auths.add(label); 392 } 393 results.clear(); 394 } 395 } finally { 396 scanner.close(); 397 } 398 return auths; 399 } 400 401 @Override 402 public List<String> getGroupAuths(String[] groups, boolean systemCall) 403 throws IOException { 404 assert (labelsRegion != null || systemCall); 405 if (systemCall || labelsRegion == null) { 406 return this.labelsCache.getGroupAuths(groups); 407 } 408 Scan s = new Scan(); 409 if (groups != null && groups.length > 0) { 410 for (String group : groups) { 411 s.addColumn(LABELS_TABLE_FAMILY, Bytes.toBytes(AuthUtil.toGroupEntry(group))); 412 } 413 } 414 Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion, 415 new Authorizations(SYSTEM_LABEL)); 416 s.setFilter(filter); 417 Set<String> auths = new HashSet<>(); 418 RegionScanner scanner = this.labelsRegion.getScanner(s); 419 try { 420 List<Cell> results = new ArrayList<>(1); 421 while (true) { 422 scanner.next(results); 423 if (results.isEmpty()) break; 424 Cell cell = results.get(0); 425 int ordinal = PrivateCellUtil.getRowAsInt(cell); 426 String label = this.labelsCache.getLabel(ordinal); 427 if (label != null) { 428 auths.add(label); 429 } 430 results.clear(); 431 } 432 } finally { 433 scanner.close(); 434 } 435 return new ArrayList<>(auths); 436 } 437 438 @Override 439 public List<String> listLabels(String regex) throws IOException { 440 assert (labelsRegion != null); 441 Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths = 442 extractLabelsAndAuths(getExistingLabelsWithAuths()); 443 Map<String, Integer> labels = labelsAndUserAuths.getFirst(); 444 labels.remove(SYSTEM_LABEL); 445 if (regex != null) { 446 Pattern pattern = Pattern.compile(regex); 447 ArrayList<String> matchedLabels = new ArrayList<>(); 448 for (String label : labels.keySet()) { 449 if (pattern.matcher(label).matches()) { 450 matchedLabels.add(label); 451 } 452 } 453 return matchedLabels; 454 } 455 return new ArrayList<>(labels.keySet()); 456 } 457 458 @Override 459 public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat, 460 boolean checkAuths) throws IOException { 461 Set<Integer> auths = new HashSet<>(); 462 if (checkAuths) { 463 User user = VisibilityUtils.getActiveUser(); 464 auths.addAll(this.labelsCache.getUserAuthsAsOrdinals(user.getShortName())); 465 auths.addAll(this.labelsCache.getGroupAuthsAsOrdinals(user.getGroupNames())); 466 } 467 return VisibilityUtils.createVisibilityExpTags(visExpression, withSerializationFormat, 468 checkAuths, auths, labelsCache); 469 } 470 471 protected void updateZk(boolean labelAddition) throws IOException { 472 // We will add to zookeeper here. 473 // TODO we should add the delta only to zk. Else this will be a very heavy op and when there are 474 // so many labels and auth in the system, we will end up adding lots of data to zk. Most 475 // possibly we will exceed zk node data limit! 476 Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths = 477 extractLabelsAndAuths(getExistingLabelsWithAuths()); 478 Map<String, Integer> existingLabels = labelsAndUserAuths.getFirst(); 479 Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond(); 480 if (labelAddition) { 481 byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(existingLabels); 482 this.labelsCache.writeToZookeeper(serialized, true); 483 } else { 484 byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths); 485 this.labelsCache.writeToZookeeper(serialized, false); 486 } 487 } 488 489 @Override 490 public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations) 491 throws IOException { 492 // If a super user issues a get/scan, he should be able to scan the cells 493 // irrespective of the Visibility labels 494 if (isReadFromSystemAuthUser()) { 495 return new VisibilityExpEvaluator() { 496 @Override 497 public boolean evaluate(Cell cell) throws IOException { 498 return true; 499 } 500 }; 501 } 502 List<String> authLabels = null; 503 for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) { 504 try { 505 // null authorizations to be handled inside SLG impl. 506 authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations); 507 authLabels = (authLabels == null) ? new ArrayList<>() : authLabels; 508 authorizations = new Authorizations(authLabels); 509 } catch (Throwable t) { 510 LOG.error(t.toString(), t); 511 throw new IOException(t); 512 } 513 } 514 int labelsCount = this.labelsCache.getLabelsCount(); 515 final BitSet bs = new BitSet(labelsCount + 1); // ordinal is index 1 based 516 if (authLabels != null) { 517 for (String authLabel : authLabels) { 518 int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabel); 519 if (labelOrdinal != 0) { 520 bs.set(labelOrdinal); 521 } 522 } 523 } 524 525 return new VisibilityExpEvaluator() { 526 @Override 527 public boolean evaluate(Cell cell) throws IOException { 528 boolean visibilityTagPresent = false; 529 Iterator<Tag> tagsItr = PrivateCellUtil.tagsIterator(cell); 530 while (tagsItr.hasNext()) { 531 boolean includeKV = true; 532 Tag tag = tagsItr.next(); 533 if (tag.getType() == VISIBILITY_TAG_TYPE) { 534 visibilityTagPresent = true; 535 int offset = tag.getValueOffset(); 536 int endOffset = offset + tag.getValueLength(); 537 while (offset < endOffset) { 538 Pair<Integer, Integer> result = TagUtil.readVIntValuePart(tag, offset); 539 int currLabelOrdinal = result.getFirst(); 540 if (currLabelOrdinal < 0) { 541 // check for the absence of this label in the Scan Auth labels 542 // ie. to check BitSet corresponding bit is 0 543 int temp = -currLabelOrdinal; 544 if (bs.get(temp)) { 545 includeKV = false; 546 break; 547 } 548 } else { 549 if (!bs.get(currLabelOrdinal)) { 550 includeKV = false; 551 break; 552 } 553 } 554 offset += result.getSecond(); 555 } 556 if (includeKV) { 557 // We got one visibility expression getting evaluated to true. Good to include this 558 // KV in the result then. 559 return true; 560 } 561 } 562 } 563 return !(visibilityTagPresent); 564 } 565 }; 566 } 567 568 protected boolean isReadFromSystemAuthUser() throws IOException { 569 User user = VisibilityUtils.getActiveUser(); 570 return havingSystemAuth(user); 571 } 572 573 @Override 574 public boolean havingSystemAuth(User user) throws IOException { 575 // A super user has 'system' auth. 576 if (Superusers.isSuperUser(user)) { 577 return true; 578 } 579 // A user can also be explicitly granted 'system' auth. 580 List<String> auths = this.getUserAuths(Bytes.toBytes(user.getShortName()), true); 581 if (LOG.isTraceEnabled()) { 582 LOG.trace("The auths for user " + user.getShortName() + " are " + auths); 583 } 584 if (auths.contains(SYSTEM_LABEL)) { 585 return true; 586 } 587 auths = this.getGroupAuths(user.getGroupNames(), true); 588 if (LOG.isTraceEnabled()) { 589 LOG.trace("The auths for groups of user " + user.getShortName() + " are " + auths); 590 } 591 return auths.contains(SYSTEM_LABEL); 592 } 593 594 @Override 595 public boolean matchVisibility(List<Tag> putVisTags, Byte putTagsFormat, List<Tag> deleteVisTags, 596 Byte deleteTagsFormat) throws IOException { 597 // Early out if there are no tags in both of cell and delete 598 if (putVisTags.isEmpty() && deleteVisTags.isEmpty()) { 599 return true; 600 } 601 // Early out if one of the tags is empty 602 if (putVisTags.isEmpty() ^ deleteVisTags.isEmpty()) { 603 return false; 604 } 605 if ((deleteTagsFormat != null && deleteTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT) 606 && (putTagsFormat == null || putTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)) { 607 if (putTagsFormat == null) { 608 return matchUnSortedVisibilityTags(putVisTags, deleteVisTags); 609 } else { 610 return matchOrdinalSortedVisibilityTags(putVisTags, deleteVisTags); 611 } 612 } 613 throw new IOException("Unexpected tag format passed for comparison, deleteTagsFormat : " 614 + deleteTagsFormat + ", putTagsFormat : " + putTagsFormat); 615 } 616 617 /** 618 * @param putVisTags Visibility tags in Put Mutation 619 * @param deleteVisTags Visibility tags in Delete Mutation 620 * @return true when all the visibility tags in Put matches with visibility tags in Delete. 621 * This is used when, at least one set of tags are not sorted based on the label ordinal. 622 */ 623 private static boolean matchUnSortedVisibilityTags(List<Tag> putVisTags, 624 List<Tag> deleteVisTags) throws IOException { 625 return compareTagsOrdinals(sortTagsBasedOnOrdinal(putVisTags), 626 sortTagsBasedOnOrdinal(deleteVisTags)); 627 } 628 629 /** 630 * @param putVisTags Visibility tags in Put Mutation 631 * @param deleteVisTags Visibility tags in Delete Mutation 632 * @return true when all the visibility tags in Put matches with visibility tags in Delete. 633 * This is used when both the set of tags are sorted based on the label ordinal. 634 */ 635 private static boolean matchOrdinalSortedVisibilityTags(List<Tag> putVisTags, 636 List<Tag> deleteVisTags) { 637 boolean matchFound = false; 638 // If the size does not match. Definitely we are not comparing the equal tags. 639 if ((deleteVisTags.size()) == putVisTags.size()) { 640 for (Tag tag : deleteVisTags) { 641 matchFound = false; 642 for (Tag givenTag : putVisTags) { 643 if (Tag.matchingValue(tag, givenTag)) { 644 matchFound = true; 645 break; 646 } 647 } 648 if (!matchFound) break; 649 } 650 } 651 return matchFound; 652 } 653 654 private static List<List<Integer>> sortTagsBasedOnOrdinal(List<Tag> tags) throws IOException { 655 List<List<Integer>> fullTagsList = new ArrayList<>(); 656 for (Tag tag : tags) { 657 if (tag.getType() == VISIBILITY_TAG_TYPE) { 658 getSortedTagOrdinals(fullTagsList, tag); 659 } 660 } 661 return fullTagsList; 662 } 663 664 private static void getSortedTagOrdinals(List<List<Integer>> fullTagsList, Tag tag) 665 throws IOException { 666 List<Integer> tagsOrdinalInSortedOrder = new ArrayList<>(); 667 int offset = tag.getValueOffset(); 668 int endOffset = offset + tag.getValueLength(); 669 while (offset < endOffset) { 670 Pair<Integer, Integer> result = TagUtil.readVIntValuePart(tag, offset); 671 tagsOrdinalInSortedOrder.add(result.getFirst()); 672 offset += result.getSecond(); 673 } 674 Collections.sort(tagsOrdinalInSortedOrder); 675 fullTagsList.add(tagsOrdinalInSortedOrder); 676 } 677 678 /* 679 * @return true when all the visibility tags in Put matches with visibility tags in Delete. 680 */ 681 private static boolean compareTagsOrdinals(List<List<Integer>> putVisTags, 682 List<List<Integer>> deleteVisTags) { 683 boolean matchFound = false; 684 if (deleteVisTags.size() == putVisTags.size()) { 685 for (List<Integer> deleteTagOrdinals : deleteVisTags) { 686 matchFound = false; 687 for (List<Integer> tagOrdinals : putVisTags) { 688 if (deleteTagOrdinals.equals(tagOrdinals)) { 689 matchFound = true; 690 break; 691 } 692 } 693 if (!matchFound) break; 694 } 695 } 696 return matchFound; 697 } 698 699 @Override 700 public byte[] encodeVisibilityForReplication(final List<Tag> tags, final Byte serializationFormat) 701 throws IOException { 702 if (tags.size() > 0 703 && (serializationFormat == null || 704 serializationFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)) { 705 return createModifiedVisExpression(tags); 706 } 707 return null; 708 } 709 710 /** 711 * @param tags 712 * - all the visibility tags associated with the current Cell 713 * @return - the modified visibility expression as byte[] 714 */ 715 private byte[] createModifiedVisExpression(final List<Tag> tags) 716 throws IOException { 717 StringBuilder visibilityString = new StringBuilder(); 718 for (Tag tag : tags) { 719 if (tag.getType() == TagType.VISIBILITY_TAG_TYPE) { 720 if (visibilityString.length() != 0) { 721 visibilityString.append(VisibilityConstants.CLOSED_PARAN).append( 722 VisibilityConstants.OR_OPERATOR); 723 } 724 int offset = tag.getValueOffset(); 725 int endOffset = offset + tag.getValueLength(); 726 boolean expressionStart = true; 727 while (offset < endOffset) { 728 Pair<Integer, Integer> result = TagUtil.readVIntValuePart(tag, offset); 729 int currLabelOrdinal = result.getFirst(); 730 if (currLabelOrdinal < 0) { 731 int temp = -currLabelOrdinal; 732 String label = this.labelsCache.getLabel(temp); 733 if (expressionStart) { 734 // Quote every label in case of unicode characters if present 735 visibilityString.append(VisibilityConstants.OPEN_PARAN) 736 .append(VisibilityConstants.NOT_OPERATOR).append(CellVisibility.quote(label)); 737 } else { 738 visibilityString.append(VisibilityConstants.AND_OPERATOR) 739 .append(VisibilityConstants.NOT_OPERATOR).append(CellVisibility.quote(label)); 740 } 741 } else { 742 String label = this.labelsCache.getLabel(currLabelOrdinal); 743 if (expressionStart) { 744 visibilityString.append(VisibilityConstants.OPEN_PARAN).append( 745 CellVisibility.quote(label)); 746 } else { 747 visibilityString.append(VisibilityConstants.AND_OPERATOR).append( 748 CellVisibility.quote(label)); 749 } 750 } 751 expressionStart = false; 752 offset += result.getSecond(); 753 } 754 } 755 } 756 if (visibilityString.length() != 0) { 757 visibilityString.append(VisibilityConstants.CLOSED_PARAN); 758 // Return the string formed as byte[] 759 return Bytes.toBytes(visibilityString.toString()); 760 } 761 return null; 762 } 763}