View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.security.visibility;
19  
20  import static org.apache.hadoop.hbase.TagType.VISIBILITY_TAG_TYPE;
21  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
22  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
23  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABEL_QUALIFIER;
24  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT;
25  import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
26  
27  import java.io.ByteArrayOutputStream;
28  import java.io.DataOutputStream;
29  import java.io.IOException;
30  import java.util.ArrayList;
31  import java.util.BitSet;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.HashSet;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.concurrent.atomic.AtomicInteger;
40  import java.util.regex.Pattern;
41  
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  import org.apache.hadoop.conf.Configuration;
45  import org.apache.hadoop.hbase.Cell;
46  import org.apache.hadoop.hbase.CellUtil;
47  import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
48  import org.apache.hadoop.hbase.Tag;
49  import org.apache.hadoop.hbase.TagType;
50  import org.apache.hadoop.hbase.classification.InterfaceAudience;
51  import org.apache.hadoop.hbase.client.Delete;
52  import org.apache.hadoop.hbase.client.Mutation;
53  import org.apache.hadoop.hbase.client.Put;
54  import org.apache.hadoop.hbase.client.Scan;
55  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
56  import org.apache.hadoop.hbase.filter.Filter;
57  import org.apache.hadoop.hbase.io.util.StreamUtils;
58  import org.apache.hadoop.hbase.regionserver.HRegion;
59  import org.apache.hadoop.hbase.regionserver.OperationStatus;
60  import org.apache.hadoop.hbase.regionserver.RegionScanner;
61  import org.apache.hadoop.hbase.security.User;
62  import org.apache.hadoop.hbase.security.access.AccessControlLists;
63  import org.apache.hadoop.hbase.util.Bytes;
64  import org.apache.hadoop.hbase.util.Pair;
65  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
66  
67  @InterfaceAudience.Private
68  public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService {
69  
70    private static final Log LOG = LogFactory.getLog(DefaultVisibilityLabelServiceImpl.class);
71  
72    // "system" label is having an ordinal value 1.
73    private static final int SYSTEM_LABEL_ORDINAL = 1;
74    private static final Tag[] LABELS_TABLE_TAGS = new Tag[1];
75    private static final byte[] DUMMY_VALUE = new byte[0];
76  
77    private AtomicInteger ordinalCounter = new AtomicInteger(-1);
78    private Configuration conf;
79    private HRegion labelsRegion;
80    private VisibilityLabelsCache labelsCache;
81    private List<ScanLabelGenerator> scanLabelGenerators;
82    private List<String> superUsers;
83    private List<String> superGroups;
84  
85    static {
86      ByteArrayOutputStream baos = new ByteArrayOutputStream();
87      DataOutputStream dos = new DataOutputStream(baos);
88      try {
89        StreamUtils.writeRawVInt32(dos, SYSTEM_LABEL_ORDINAL);
90      } catch (IOException e) {
91        // We write to a byte array. No Exception can happen.
92      }
93      LABELS_TABLE_TAGS[0] = new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
94    }
95  
96    public DefaultVisibilityLabelServiceImpl() {
97  
98    }
99  
100   @Override
101   public void setConf(Configuration conf) {
102     this.conf = conf;
103   }
104 
105   @Override
106   public Configuration getConf() {
107     return this.conf;
108   }
109 
110   @Override
111   public void init(RegionCoprocessorEnvironment e) throws IOException {
112     ZooKeeperWatcher zk = e.getRegionServerServices().getZooKeeper();
113     try {
114       labelsCache = VisibilityLabelsCache.createAndGet(zk, this.conf);
115     } catch (IOException ioe) {
116       LOG.error("Error creating VisibilityLabelsCache", ioe);
117       throw ioe;
118     }
119     this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
120     Pair<List<String>, List<String>> superUsersAndGroups =
121         VisibilityUtils.getSystemAndSuperUsers(this.conf);
122     this.superUsers = superUsersAndGroups.getFirst();
123     this.superGroups = superUsersAndGroups.getSecond();
124     if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
125       this.labelsRegion = e.getRegion();
126       Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
127           extractLabelsAndAuths(getExistingLabelsWithAuths());
128       Map<String, Integer> labels = labelsAndUserAuths.getFirst();
129       Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
130       // Add the "system" label if it is not added into the system yet
131       addSystemLabel(this.labelsRegion, labels, userAuths);
132       int ordinal = SYSTEM_LABEL_ORDINAL; // Ordinal 1 is reserved for "system" label.
133       for (Integer i : labels.values()) {
134         if (i > ordinal) {
135           ordinal = i;
136         }
137       }
138       this.ordinalCounter.set(ordinal + 1);
139       if (labels.size() > 0) {
140         // If there is no data need not write to zk
141         byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(labels);
142         this.labelsCache.writeToZookeeper(serialized, true);
143         this.labelsCache.refreshLabelsCache(serialized);
144       }
145       if (userAuths.size() > 0) {
146         byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
147         this.labelsCache.writeToZookeeper(serialized, false);
148         this.labelsCache.refreshUserAuthsCache(serialized);
149       }
150     }
151   }
152 
153   protected List<List<Cell>> getExistingLabelsWithAuths() throws IOException {
154     Scan scan = new Scan();
155     RegionScanner scanner = labelsRegion.getScanner(scan);
156     List<List<Cell>> existingLabels = new ArrayList<List<Cell>>();
157     try {
158       while (true) {
159         List<Cell> cells = new ArrayList<Cell>();
160         scanner.next(cells);
161         if (cells.isEmpty()) {
162           break;
163         }
164         existingLabels.add(cells);
165       }
166     } finally {
167       scanner.close();
168     }
169     return existingLabels;
170   }
171 
172   protected Pair<Map<String, Integer>, Map<String, List<Integer>>> extractLabelsAndAuths(
173       List<List<Cell>> labelDetails) {
174     Map<String, Integer> labels = new HashMap<String, Integer>();
175     Map<String, List<Integer>> userAuths = new HashMap<String, List<Integer>>();
176     for (List<Cell> cells : labelDetails) {
177       for (Cell cell : cells) {
178         if (Bytes.equals(cell.getQualifierArray(), cell.getQualifierOffset(),
179             cell.getQualifierLength(), LABEL_QUALIFIER, 0, LABEL_QUALIFIER.length)) {
180           labels.put(
181               Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()),
182               Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
183         } else {
184           // These are user cells who has authorization for this label
185           String user = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
186               cell.getQualifierLength());
187           List<Integer> auths = userAuths.get(user);
188           if (auths == null) {
189             auths = new ArrayList<Integer>();
190             userAuths.put(user, auths);
191           }
192           auths.add(Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
193         }
194       }
195     }
196     return new Pair<Map<String, Integer>, Map<String, List<Integer>>>(labels, userAuths);
197   }
198 
199   protected void addSystemLabel(HRegion region, Map<String, Integer> labels,
200       Map<String, List<Integer>> userAuths) throws IOException {
201     if (!labels.containsKey(SYSTEM_LABEL)) {
202       Put p = new Put(Bytes.toBytes(SYSTEM_LABEL_ORDINAL));
203       p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, Bytes.toBytes(SYSTEM_LABEL));
204       region.put(p);
205       labels.put(SYSTEM_LABEL, SYSTEM_LABEL_ORDINAL);
206     }
207   }
208 
209   @Override
210   public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
211     assert labelsRegion != null;
212     OperationStatus[] finalOpStatus = new OperationStatus[labels.size()];
213     List<Mutation> puts = new ArrayList<Mutation>(labels.size());
214     int i = 0;
215     for (byte[] label : labels) {
216       String labelStr = Bytes.toString(label);
217       if (this.labelsCache.getLabelOrdinal(labelStr) > 0) {
218         finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
219             new LabelAlreadyExistsException("Label '" + labelStr + "' already exists"));
220       } else {
221         Put p = new Put(Bytes.toBytes(ordinalCounter.get()));
222         p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, label, LABELS_TABLE_TAGS);
223         if (LOG.isDebugEnabled()) {
224           LOG.debug("Adding the label " + labelStr);
225         }
226         puts.add(p);
227         ordinalCounter.incrementAndGet();
228       }
229       i++;
230     }
231     if (mutateLabelsRegion(puts, finalOpStatus)) {
232       updateZk(true);
233     }
234     return finalOpStatus;
235   }
236 
237   @Override
238   public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
239     assert labelsRegion != null;
240     OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
241     List<Mutation> puts = new ArrayList<Mutation>(authLabels.size());
242     int i = 0;
243     for (byte[] auth : authLabels) {
244       String authStr = Bytes.toString(auth);
245       int labelOrdinal = this.labelsCache.getLabelOrdinal(authStr);
246       if (labelOrdinal == 0) {
247         // This label is not yet added. 1st this should be added to the system
248         finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
249             new InvalidLabelException("Label '" + authStr + "' doesn't exists"));
250       } else {
251         Put p = new Put(Bytes.toBytes(labelOrdinal));
252         p.addImmutable(LABELS_TABLE_FAMILY, user, DUMMY_VALUE, LABELS_TABLE_TAGS);
253         puts.add(p);
254       }
255       i++;
256     }
257     if (mutateLabelsRegion(puts, finalOpStatus)) {
258       updateZk(false);
259     }
260     return finalOpStatus;
261   }
262 
263   @Override
264   public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
265     assert labelsRegion != null;
266     OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
267     List<String> currentAuths;
268     if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) {
269       String group = AccessControlLists.getGroupName(Bytes.toString(user));
270       currentAuths = this.getGroupAuths(new String[]{group}, true);
271     }
272     else {
273       currentAuths = this.getUserAuths(user, true);
274     }
275     List<Mutation> deletes = new ArrayList<Mutation>(authLabels.size());
276     int i = 0;
277     for (byte[] authLabel : authLabels) {
278       String authLabelStr = Bytes.toString(authLabel);
279       if (currentAuths.contains(authLabelStr)) {
280         int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabelStr);
281         assert labelOrdinal > 0;
282         Delete d = new Delete(Bytes.toBytes(labelOrdinal));
283         d.deleteColumns(LABELS_TABLE_FAMILY, user);
284         deletes.add(d);
285       } else {
286         // This label is not set for the user.
287         finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
288             new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
289                 + Bytes.toString(user)));
290       }
291       i++;
292     }
293     if (mutateLabelsRegion(deletes, finalOpStatus)) {
294       updateZk(false);
295     }
296     return finalOpStatus;
297   }
298 
299   /**
300    * Adds the mutations to labels region and set the results to the finalOpStatus. finalOpStatus
301    * might have some entries in it where the OpStatus is FAILURE. We will leave those and set in
302    * others in the order.
303    * @param mutations
304    * @param finalOpStatus
305    * @return whether we need a ZK update or not.
306    */
307   private boolean mutateLabelsRegion(List<Mutation> mutations, OperationStatus[] finalOpStatus)
308       throws IOException {
309     OperationStatus[] opStatus = this.labelsRegion.batchMutate(mutations
310         .toArray(new Mutation[mutations.size()]));
311     int i = 0;
312     boolean updateZk = false;
313     for (OperationStatus status : opStatus) {
314       // Update the zk when atleast one of the mutation was added successfully.
315       updateZk = updateZk || (status.getOperationStatusCode() == OperationStatusCode.SUCCESS);
316       for (; i < finalOpStatus.length; i++) {
317         if (finalOpStatus[i] == null) {
318           finalOpStatus[i] = status;
319           break;
320         }
321       }
322     }
323     return updateZk;
324   }
325 
326   @Override
327   @Deprecated
328   public List<String> getAuths(byte[] user, boolean systemCall)
329       throws IOException {
330     return getUserAuths(user, systemCall);
331   }
332 
333   @Override
334   public List<String> getUserAuths(byte[] user, boolean systemCall)
335       throws IOException {
336     assert (labelsRegion != null || systemCall);
337     if (systemCall || labelsRegion == null) {
338       return this.labelsCache.getUserAuths(Bytes.toString(user));
339     }
340     Scan s = new Scan();
341     if (user != null && user.length > 0) {
342       s.addColumn(LABELS_TABLE_FAMILY, user);
343     }
344     Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion,
345         new Authorizations(SYSTEM_LABEL));
346     s.setFilter(filter);
347     ArrayList<String> auths = new ArrayList<String>();
348     RegionScanner scanner = this.labelsRegion.getScanner(s);
349     try {
350       List<Cell> results = new ArrayList<Cell>(1);
351       while (true) {
352         scanner.next(results);
353         if (results.isEmpty()) break;
354         Cell cell = results.get(0);
355         int ordinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
356         String label = this.labelsCache.getLabel(ordinal);
357         if (label != null) {
358           auths.add(label);
359         }
360         results.clear();
361       }
362     } finally {
363       scanner.close();
364     }
365     return auths;
366   }
367 
368   @Override
369   public List<String> getGroupAuths(String[] groups, boolean systemCall)
370       throws IOException {
371     assert (labelsRegion != null || systemCall);
372     if (systemCall || labelsRegion == null) {
373       return this.labelsCache.getGroupAuths(groups);
374     }
375     Scan s = new Scan();
376     if (groups != null && groups.length > 0) {
377       for (String group : groups) {
378         s.addColumn(LABELS_TABLE_FAMILY, Bytes.toBytes(AccessControlLists.toGroupEntry(group)));
379       }
380     }
381     Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion,
382         new Authorizations(SYSTEM_LABEL));
383     s.setFilter(filter);
384     Set<String> auths = new HashSet<String>();
385     RegionScanner scanner = this.labelsRegion.getScanner(s);
386     try {
387       List<Cell> results = new ArrayList<Cell>(1);
388       while (true) {
389         scanner.next(results);
390         if (results.isEmpty()) break;
391         Cell cell = results.get(0);
392         int ordinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
393         String label = this.labelsCache.getLabel(ordinal);
394         if (label != null) {
395           auths.add(label);
396         }
397         results.clear();
398       }
399     } finally {
400       scanner.close();
401     }
402     return new ArrayList<String>(auths);
403   }
404 
405   @Override
406   public List<String> listLabels(String regex) throws IOException {
407     assert (labelsRegion != null);
408     Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
409         extractLabelsAndAuths(getExistingLabelsWithAuths());
410     Map<String, Integer> labels = labelsAndUserAuths.getFirst();
411     labels.remove(SYSTEM_LABEL);
412     if (regex != null) {
413       Pattern pattern = Pattern.compile(regex);
414       ArrayList<String> matchedLabels = new ArrayList<String>();
415       for (String label : labels.keySet()) {
416         if (pattern.matcher(label).matches()) {
417           matchedLabels.add(label);
418         }
419       }
420       return matchedLabels;
421     }
422     return new ArrayList<String>(labels.keySet());
423   }
424 
425   @Override
426   public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
427       boolean checkAuths) throws IOException {
428     Set<Integer> auths = new HashSet<Integer>();
429     if (checkAuths) {
430       User user = VisibilityUtils.getActiveUser();
431       auths.addAll(this.labelsCache.getUserAuthsAsOrdinals(user.getShortName()));
432       auths.addAll(this.labelsCache.getGroupAuthsAsOrdinals(user.getGroupNames()));
433     }
434     return VisibilityUtils.createVisibilityExpTags(visExpression, withSerializationFormat,
435         checkAuths, auths, labelsCache);
436   }
437 
438   protected void updateZk(boolean labelAddition) throws IOException {
439     // We will add to zookeeper here.
440     // TODO we should add the delta only to zk. Else this will be a very heavy op and when there are
441     // so many labels and auth in the system, we will end up adding lots of data to zk. Most
442     // possibly we will exceed zk node data limit!
443     Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
444         extractLabelsAndAuths(getExistingLabelsWithAuths());
445     Map<String, Integer> existingLabels = labelsAndUserAuths.getFirst();
446     Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
447     if (labelAddition) {
448       byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(existingLabels);
449       this.labelsCache.writeToZookeeper(serialized, true);
450     } else {
451       byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
452       this.labelsCache.writeToZookeeper(serialized, false);
453     }
454   }
455 
456   @Override
457   public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
458       throws IOException {
459     // If a super user issues a get/scan, he should be able to scan the cells
460     // irrespective of the Visibility labels
461     if (isReadFromSystemAuthUser()) {
462       return new VisibilityExpEvaluator() {
463         @Override
464         public boolean evaluate(Cell cell) throws IOException {
465           return true;
466         }
467       };
468     }
469     List<String> authLabels = null;
470     for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
471       try {
472         // null authorizations to be handled inside SLG impl.
473         authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
474         authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
475         authorizations = new Authorizations(authLabels);
476       } catch (Throwable t) {
477         LOG.error(t);
478         throw new IOException(t);
479       }
480     }
481     int labelsCount = this.labelsCache.getLabelsCount();
482     final BitSet bs = new BitSet(labelsCount + 1); // ordinal is index 1 based
483     if (authLabels != null) {
484       for (String authLabel : authLabels) {
485         int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabel);
486         if (labelOrdinal != 0) {
487           bs.set(labelOrdinal);
488         }
489       }
490     }
491 
492     return new VisibilityExpEvaluator() {
493       @Override
494       public boolean evaluate(Cell cell) throws IOException {
495         boolean visibilityTagPresent = false;
496         // Save an object allocation where we can
497         if (cell.getTagsLength() > 0) {
498           Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
499               cell.getTagsLength());
500           while (tagsItr.hasNext()) {
501             boolean includeKV = true;
502             Tag tag = tagsItr.next();
503             if (tag.getType() == VISIBILITY_TAG_TYPE) {
504               visibilityTagPresent = true;
505               int offset = tag.getTagOffset();
506               int endOffset = offset + tag.getTagLength();
507               while (offset < endOffset) {
508                 Pair<Integer, Integer> result = StreamUtils
509                     .readRawVarint32(tag.getBuffer(), offset);
510                 int currLabelOrdinal = result.getFirst();
511                 if (currLabelOrdinal < 0) {
512                   // check for the absence of this label in the Scan Auth labels
513                   // ie. to check BitSet corresponding bit is 0
514                   int temp = -currLabelOrdinal;
515                   if (bs.get(temp)) {
516                     includeKV = false;
517                     break;
518                   }
519                 } else {
520                   if (!bs.get(currLabelOrdinal)) {
521                     includeKV = false;
522                     break;
523                   }
524                 }
525                 offset += result.getSecond();
526               }
527               if (includeKV) {
528                 // We got one visibility expression getting evaluated to true. Good to include this
529                 // KV in the result then.
530                 return true;
531               }
532             }
533           }
534         }
535         return !(visibilityTagPresent);
536       }
537     };
538   }
539 
540   protected boolean isReadFromSystemAuthUser() throws IOException {
541     User user = VisibilityUtils.getActiveUser();
542     return havingSystemAuth(user);
543   }
544 
545   @Override
546   @Deprecated
547   public boolean havingSystemAuth(byte[] user) throws IOException {
548     // Implementation for backward compatibility
549     if (this.superUsers.contains(Bytes.toString(user))) {
550       return true;
551     }
552     List<String> auths = this.getUserAuths(user, true);
553     if (LOG.isTraceEnabled()) {
554       LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths);
555     }
556     return auths.contains(SYSTEM_LABEL);
557   }
558 
559   @Override
560   public boolean havingSystemAuth(User user) throws IOException {
561     // A super user has 'system' auth.
562     if (isSystemOrSuperUser(user)) {
563       return true;
564     }
565     // A user can also be explicitly granted 'system' auth.
566     List<String> auths = this.getUserAuths(Bytes.toBytes(user.getShortName()), true);
567     if (LOG.isTraceEnabled()) {
568       LOG.trace("The auths for user " + user.getShortName() + " are " + auths);
569     }
570     if (auths.contains(SYSTEM_LABEL)) {
571       return true;
572     }
573     auths = this.getGroupAuths(user.getGroupNames(), true);
574     if (LOG.isTraceEnabled()) {
575       LOG.trace("The auths for groups of user " + user.getShortName() + " are " + auths);
576     }
577     return auths.contains(SYSTEM_LABEL);
578   }
579 
580   private boolean isSystemOrSuperUser(User user) throws IOException {
581     if (this.superUsers.contains(user.getShortName())) {
582       return true;
583     }
584     String[] groups = user.getGroupNames();
585     if (groups != null && groups.length > 0) {
586       for (String group : groups) {
587         if (this.superGroups.contains(group)) {
588           return true;
589         }
590       }
591     }
592     return false;
593   }
594 
595   @Override
596   public boolean matchVisibility(List<Tag> putVisTags, Byte putTagsFormat, List<Tag> deleteVisTags,
597       Byte deleteTagsFormat) throws IOException {
598     if ((deleteTagsFormat != null && deleteTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)
599         && (putTagsFormat == null || putTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)) {
600       if (putVisTags.size() == 0) {
601         // Early out if there are no tags in the cell
602         return false;
603       }
604       if (putTagsFormat == null) {
605         return matchUnSortedVisibilityTags(putVisTags, deleteVisTags);
606       } else {
607         return matchOrdinalSortedVisibilityTags(putVisTags, deleteVisTags);
608       }
609     }
610     throw new IOException("Unexpected tag format passed for comparison, deleteTagsFormat : "
611         + deleteTagsFormat + ", putTagsFormat : " + putTagsFormat);
612   }
613 
614   /**
615    * @param putVisTags Visibility tags in Put Mutation
616    * @param deleteVisTags Visibility tags in Delete Mutation
617    * @return true when all the visibility tags in Put matches with visibility tags in Delete.
618    * This is used when, at least one set of tags are not sorted based on the label ordinal.
619    */
620   private static boolean matchUnSortedVisibilityTags(List<Tag> putVisTags,
621       List<Tag> deleteVisTags) throws IOException {
622     return compareTagsOrdinals(sortTagsBasedOnOrdinal(putVisTags),
623         sortTagsBasedOnOrdinal(deleteVisTags));
624   }
625 
626   /**
627    * @param putVisTags Visibility tags in Put Mutation
628    * @param deleteVisTags Visibility tags in Delete Mutation
629    * @return true when all the visibility tags in Put matches with visibility tags in Delete.
630    * This is used when both the set of tags are sorted based on the label ordinal.
631    */
632   private static boolean matchOrdinalSortedVisibilityTags(List<Tag> putVisTags,
633       List<Tag> deleteVisTags) {
634     boolean matchFound = false;
635     // If the size does not match. Definitely we are not comparing the equal tags.
636     if ((deleteVisTags.size()) == putVisTags.size()) {
637       for (Tag tag : deleteVisTags) {
638         matchFound = false;
639         for (Tag givenTag : putVisTags) {
640           if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
641               givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
642             matchFound = true;
643             break;
644           }
645         }
646         if (!matchFound) break;
647       }
648     }
649     return matchFound;
650   }
651 
652   private static List<List<Integer>> sortTagsBasedOnOrdinal(List<Tag> tags) throws IOException {
653     List<List<Integer>> fullTagsList = new ArrayList<List<Integer>>();
654     for (Tag tag : tags) {
655       if (tag.getType() == VISIBILITY_TAG_TYPE) {
656         getSortedTagOrdinals(fullTagsList, tag);
657       }
658     }
659     return fullTagsList;
660   }
661 
662   private static void getSortedTagOrdinals(List<List<Integer>> fullTagsList, Tag tag)
663       throws IOException {
664     List<Integer> tagsOrdinalInSortedOrder = new ArrayList<Integer>();
665     int offset = tag.getTagOffset();
666     int endOffset = offset + tag.getTagLength();
667     while (offset < endOffset) {
668       Pair<Integer, Integer> result = StreamUtils.readRawVarint32(tag.getBuffer(), offset);
669       tagsOrdinalInSortedOrder.add(result.getFirst());
670       offset += result.getSecond();
671     }
672     Collections.sort(tagsOrdinalInSortedOrder);
673     fullTagsList.add(tagsOrdinalInSortedOrder);
674   }
675 
676   /*
677    * @return true when all the visibility tags in Put matches with visibility tags in Delete.
678    */
679   private static boolean compareTagsOrdinals(List<List<Integer>> putVisTags,
680       List<List<Integer>> deleteVisTags) {
681     boolean matchFound = false;
682     if (deleteVisTags.size() == putVisTags.size()) {
683       for (List<Integer> deleteTagOrdinals : deleteVisTags) {
684         matchFound = false;
685         for (List<Integer> tagOrdinals : putVisTags) {
686           if (deleteTagOrdinals.equals(tagOrdinals)) {
687             matchFound = true;
688             break;
689           }
690         }
691         if (!matchFound) break;
692       }
693     }
694     return matchFound;
695   }
696 
697   @Override
698   public byte[] encodeVisibilityForReplication(final List<Tag> tags, final Byte serializationFormat)
699       throws IOException {
700     if (tags.size() > 0
701         && (serializationFormat == null ||
702         serializationFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)) {
703       return createModifiedVisExpression(tags);
704     }
705     return null;
706   }
707 
708   /**
709    * @param tags
710    *          - all the visibility tags associated with the current Cell
711    * @return - the modified visibility expression as byte[]
712    */
713   private byte[] createModifiedVisExpression(final List<Tag> tags)
714       throws IOException {
715     StringBuilder visibilityString = new StringBuilder();
716     for (Tag tag : tags) {
717       if (tag.getType() == TagType.VISIBILITY_TAG_TYPE) {
718         if (visibilityString.length() != 0) {
719           visibilityString.append(VisibilityConstants.CLOSED_PARAN).append(
720               VisibilityConstants.OR_OPERATOR);
721         }
722         int offset = tag.getTagOffset();
723         int endOffset = offset + tag.getTagLength();
724         boolean expressionStart = true;
725         while (offset < endOffset) {
726           Pair<Integer, Integer> result = StreamUtils.readRawVarint32(tag.getBuffer(), offset);
727           int currLabelOrdinal = result.getFirst();
728           if (currLabelOrdinal < 0) {
729             int temp = -currLabelOrdinal;
730             String label = this.labelsCache.getLabel(temp);
731             if (expressionStart) {
732               // Quote every label in case of unicode characters if present
733               visibilityString.append(VisibilityConstants.OPEN_PARAN)
734                   .append(VisibilityConstants.NOT_OPERATOR).append(CellVisibility.quote(label));
735             } else {
736               visibilityString.append(VisibilityConstants.AND_OPERATOR)
737                   .append(VisibilityConstants.NOT_OPERATOR).append(CellVisibility.quote(label));
738             }
739           } else {
740             String label = this.labelsCache.getLabel(currLabelOrdinal);
741             if (expressionStart) {
742               visibilityString.append(VisibilityConstants.OPEN_PARAN).append(
743                   CellVisibility.quote(label));
744             } else {
745               visibilityString.append(VisibilityConstants.AND_OPERATOR).append(
746                   CellVisibility.quote(label));
747             }
748           }
749           expressionStart = false;
750           offset += result.getSecond();
751         }
752       }
753     }
754     if (visibilityString.length() != 0) {
755       visibilityString.append(VisibilityConstants.CLOSED_PARAN);
756       // Return the string formed as byte[]
757       return Bytes.toBytes(visibilityString.toString());
758     }
759     return null;
760   }
761 }