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}