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  
19  package org.apache.hadoop.hbase.security.visibility;
20  
21  import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SANITY_CHECK_FAILURE;
22  import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SUCCESS;
23  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
24  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
25  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABEL_QUALIFIER;
26  import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
27  
28  import java.io.ByteArrayOutputStream;
29  import java.io.DataOutputStream;
30  import java.io.IOException;
31  import java.util.ArrayList;
32  import java.util.BitSet;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.classification.InterfaceAudience;
42  import org.apache.hadoop.conf.Configuration;
43  import org.apache.hadoop.hbase.Cell;
44  import org.apache.hadoop.hbase.CellScanner;
45  import org.apache.hadoop.hbase.CellUtil;
46  import org.apache.hadoop.hbase.CoprocessorEnvironment;
47  import org.apache.hadoop.hbase.DoNotRetryIOException;
48  import org.apache.hadoop.hbase.HColumnDescriptor;
49  import org.apache.hadoop.hbase.HConstants;
50  import org.apache.hadoop.hbase.HRegionInfo;
51  import org.apache.hadoop.hbase.HTableDescriptor;
52  import org.apache.hadoop.hbase.KeyValue;
53  import org.apache.hadoop.hbase.KeyValue.Type;
54  import org.apache.hadoop.hbase.KeyValueUtil;
55  import org.apache.hadoop.hbase.NamespaceDescriptor;
56  import org.apache.hadoop.hbase.ServerName;
57  import org.apache.hadoop.hbase.TableName;
58  import org.apache.hadoop.hbase.Tag;
59  import org.apache.hadoop.hbase.catalog.MetaReader;
60  import org.apache.hadoop.hbase.client.Append;
61  import org.apache.hadoop.hbase.client.Delete;
62  import org.apache.hadoop.hbase.client.Get;
63  import org.apache.hadoop.hbase.client.Increment;
64  import org.apache.hadoop.hbase.client.Mutation;
65  import org.apache.hadoop.hbase.client.Put;
66  import org.apache.hadoop.hbase.client.Result;
67  import org.apache.hadoop.hbase.client.Scan;
68  import org.apache.hadoop.hbase.constraint.ConstraintException;
69  import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
70  import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
71  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
72  import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
73  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
74  import org.apache.hadoop.hbase.coprocessor.MasterObserver;
75  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
76  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
77  import org.apache.hadoop.hbase.coprocessor.RegionObserver;
78  import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
79  import org.apache.hadoop.hbase.exceptions.DeserializationException;
80  import org.apache.hadoop.hbase.exceptions.FailedSanityCheckException;
81  import org.apache.hadoop.hbase.filter.Filter;
82  import org.apache.hadoop.hbase.filter.FilterBase;
83  import org.apache.hadoop.hbase.filter.FilterList;
84  import org.apache.hadoop.hbase.io.hfile.HFile;
85  import org.apache.hadoop.hbase.io.util.StreamUtils;
86  import org.apache.hadoop.hbase.ipc.RequestContext;
87  import org.apache.hadoop.hbase.master.MasterServices;
88  import org.apache.hadoop.hbase.master.RegionPlan;
89  import org.apache.hadoop.hbase.protobuf.ResponseConverter;
90  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
91  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
92  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos;
93  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsRequest;
94  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
95  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.SetAuthsRequest;
96  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel;
97  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest;
98  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
99  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService;
100 import org.apache.hadoop.hbase.regionserver.BloomType;
101 import org.apache.hadoop.hbase.regionserver.DeleteTracker;
102 import org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy;
103 import org.apache.hadoop.hbase.regionserver.HRegion;
104 import org.apache.hadoop.hbase.regionserver.InternalScanner;
105 import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress;
106 import org.apache.hadoop.hbase.regionserver.OperationStatus;
107 import org.apache.hadoop.hbase.regionserver.RegionScanner;
108 import org.apache.hadoop.hbase.security.AccessDeniedException;
109 import org.apache.hadoop.hbase.security.User;
110 import org.apache.hadoop.hbase.security.access.AccessControlLists;
111 import org.apache.hadoop.hbase.security.access.AccessController;
112 import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
113 import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
114 import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
115 import org.apache.hadoop.hbase.security.visibility.expression.Operator;
116 import org.apache.hadoop.hbase.util.ByteRange;
117 import org.apache.hadoop.hbase.util.Bytes;
118 import org.apache.hadoop.hbase.util.Pair;
119 import org.apache.hadoop.hbase.util.SimpleByteRange;
120 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
121 
122 import com.google.common.collect.Lists;
123 import com.google.common.collect.MapMaker;
124 import com.google.protobuf.ByteString;
125 import org.apache.hadoop.hbase.util.ByteStringer;
126 import com.google.protobuf.RpcCallback;
127 import com.google.protobuf.RpcController;
128 import com.google.protobuf.Service;
129 
130 /**
131  * Coprocessor that has both the MasterObserver and RegionObserver implemented that supports in
132  * visibility labels
133  */
134 @InterfaceAudience.Private
135 public class VisibilityController extends BaseRegionObserver implements MasterObserver,
136     RegionObserver, VisibilityLabelsService.Interface, CoprocessorService {
137 
138   private static final Log LOG = LogFactory.getLog(VisibilityController.class);
139   private static final byte[] DUMMY_VALUE = new byte[0];
140   // "system" label is having an ordinal value 1.
141   private static final int SYSTEM_LABEL_ORDINAL = 1;
142   private static final Tag[] LABELS_TABLE_TAGS = new Tag[1];
143 
144   private final ExpressionParser expressionParser = new ExpressionParser();
145   private final ExpressionExpander expressionExpander = new ExpressionExpander();
146   private VisibilityLabelsManager visibilityManager;
147   // defined only for Endpoint implementation, so it can have way to access region services.
148   private RegionCoprocessorEnvironment regionEnv;
149   private List<ScanLabelGenerator> scanLabelGenerators;
150   
151   private volatile int ordinalCounter = -1;
152   // flags if we are running on a region of the 'labels' table
153   private boolean labelsRegion = false;
154   // Flag denoting whether AcessController is available or not.
155   private boolean acOn = false;
156   private Configuration conf;
157   private volatile boolean initialized = false;
158   /** Mapping of scanner instances to the user who created them */
159   private Map<InternalScanner,String> scannerOwners =
160       new MapMaker().weakKeys().makeMap();
161 
162   List<String> superUsers;
163 
164   static {
165     ByteArrayOutputStream baos = new ByteArrayOutputStream();
166     DataOutputStream dos = new DataOutputStream(baos);
167     try {
168       StreamUtils.writeRawVInt32(dos, SYSTEM_LABEL_ORDINAL);
169     } catch (IOException e) {
170       // We write to a byte array. No Exception can happen.
171     }
172     LABELS_TABLE_TAGS[0] = new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray());
173   }
174 
175   // Add to this list if there are any reserved tag types
176   private static ArrayList<Byte> reservedVisTagTypes = new ArrayList<Byte>();
177   static {
178     reservedVisTagTypes.add(VisibilityUtils.VISIBILITY_TAG_TYPE);
179     reservedVisTagTypes.add(VisibilityUtils.VISIBILITY_EXP_SERIALIZATION_TAG_TYPE);
180   }
181 
182   @Override
183   public void start(CoprocessorEnvironment env) throws IOException {
184     this.conf = env.getConfiguration();
185     if (HFile.getFormatVersion(conf) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) {
186       throw new RuntimeException("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS
187         + " is required to persist visibility labels. Consider setting " + HFile.FORMAT_VERSION_KEY
188         + " accordingly.");
189     }
190     ZooKeeperWatcher zk = null;
191     if (env instanceof MasterCoprocessorEnvironment) {
192       // if running on HMaster
193       MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) env;
194       zk = mEnv.getMasterServices().getZooKeeper();
195     } else if (env instanceof RegionCoprocessorEnvironment) {
196       // if running at region
197       regionEnv = (RegionCoprocessorEnvironment) env;
198       zk = regionEnv.getRegionServerServices().getZooKeeper();
199     } else if (env instanceof RegionServerCoprocessorEnvironment) {
200       throw new RuntimeException(
201           "Visibility controller should not be configured as " +
202           "'hbase.coprocessor.regionserver.classes'.");
203     }
204 
205     // If zk is null or IOException while obtaining auth manager,
206     // throw RuntimeException so that the coprocessor is unloaded.
207     if (zk == null) {
208       throw new RuntimeException("Error obtaining VisibilityLabelsManager, zk found null.");
209     }
210     try {
211       this.visibilityManager = VisibilityLabelsManager.get(zk, this.conf);
212     } catch (IOException ioe) {
213       throw new RuntimeException("Error obtaining VisibilityLabelsManager", ioe);
214     }
215     if (env instanceof RegionCoprocessorEnvironment) {
216       // ScanLabelGenerator to be instantiated only with Region Observer.
217       scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
218     }
219     this.superUsers = getSystemAndSuperUsers();
220   }
221 
222   @Override
223   public void stop(CoprocessorEnvironment env) throws IOException {
224 
225   }
226 
227   /********************************* Master related hooks **********************************/
228 
229   @Override
230   public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
231     // Need to create the new system table for labels here
232     MasterServices master = ctx.getEnvironment().getMasterServices();
233     if (!MetaReader.tableExists(master.getCatalogTracker(), LABELS_TABLE_NAME)) {
234       HTableDescriptor labelsTable = new HTableDescriptor(LABELS_TABLE_NAME);
235       HColumnDescriptor labelsColumn = new HColumnDescriptor(LABELS_TABLE_FAMILY);
236       labelsColumn.setBloomFilterType(BloomType.NONE);
237       labelsColumn.setBlockCacheEnabled(false); // We will cache all the labels. No need of normal
238                                                  // table block cache.
239       labelsTable.addFamily(labelsColumn);
240       // Let the "labels" table having only one region always. We are not expecting too many labels in
241       // the system.
242       labelsTable.setValue(HTableDescriptor.SPLIT_POLICY,
243           DisabledRegionSplitPolicy.class.getName());
244       labelsTable.setValue(Bytes.toBytes(HConstants.DISALLOW_WRITES_IN_RECOVERING),
245           Bytes.toBytes(true));
246       master.createTable(labelsTable, null);
247     }
248   }
249 
250   @Override
251   public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
252       HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
253   }
254 
255   @Override
256   public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
257       HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
258   }
259 
260   @Override
261   public void preCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
262       HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
263   }
264 
265   @Override
266   public void postCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
267       HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
268   }
269 
270   @Override
271   public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
272       throws IOException {
273   }
274 
275   @Override
276   public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
277       throws IOException {
278   }
279 
280   @Override
281   public void preDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
282       TableName tableName) throws IOException {
283   }
284 
285   @Override
286   public void postDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
287       TableName tableName) throws IOException {
288   }
289 
290   @Override
291   public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
292       TableName tableName, HTableDescriptor htd) throws IOException {
293     if (LABELS_TABLE_NAME.equals(tableName)) {
294       throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
295     }
296   }
297 
298   @Override
299   public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
300       TableName tableName, HTableDescriptor htd) throws IOException {
301   }
302 
303   @Override
304   public void preModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
305       TableName tableName, HTableDescriptor htd) throws IOException {
306   }
307 
308   @Override
309   public void postModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
310       TableName tableName, HTableDescriptor htd) throws IOException {
311   }
312 
313   @Override
314   public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName,
315       HColumnDescriptor column) throws IOException {
316     if (LABELS_TABLE_NAME.equals(tableName)) {
317       throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
318     }
319   }
320 
321   @Override
322   public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName,
323       HColumnDescriptor column) throws IOException {
324   }
325 
326   @Override
327   public void preAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
328       TableName tableName, HColumnDescriptor column) throws IOException {
329   }
330 
331   @Override
332   public void postAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
333       TableName tableName, HColumnDescriptor column) throws IOException {
334   }
335 
336   @Override
337   public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
338       TableName tableName, HColumnDescriptor descriptor) throws IOException {
339     if (LABELS_TABLE_NAME.equals(tableName)) {
340       throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
341     }
342   }
343 
344   @Override
345   public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
346       TableName tableName, HColumnDescriptor descriptor) throws IOException {
347   }
348 
349   @Override
350   public void preModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
351       TableName tableName, HColumnDescriptor descriptor) throws IOException {
352   }
353 
354   @Override
355   public void postModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
356       TableName tableName, HColumnDescriptor descriptor) throws IOException {
357   }
358 
359   @Override
360   public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
361       TableName tableName, byte[] c) throws IOException {
362     if (LABELS_TABLE_NAME.equals(tableName)) {
363       throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME);
364     }
365   }
366 
367   @Override
368   public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx,
369       TableName tableName, byte[] c) throws IOException {
370   }
371 
372   @Override
373   public void preDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
374       TableName tableName, byte[] c) throws IOException {
375   }
376 
377   @Override
378   public void postDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
379       TableName tableName, byte[] c) throws IOException {
380   }
381 
382   @Override
383   public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
384       throws IOException {
385   }
386 
387   @Override
388   public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
389       throws IOException {
390   }
391 
392   @Override
393   public void preEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
394       TableName tableName) throws IOException {
395   }
396 
397   @Override
398   public void postEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
399       TableName tableName) throws IOException {
400   }
401 
402   @Override
403   public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
404       throws IOException {
405     if (LABELS_TABLE_NAME.equals(tableName)) {
406       throw new ConstraintException("Cannot disable " + LABELS_TABLE_NAME);
407     }
408   }
409 
410   @Override
411   public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
412       TableName tableName) throws IOException {
413   }
414 
415   @Override
416   public void preDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
417       TableName tableName) throws IOException {
418   }
419 
420   @Override
421   public void postDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
422       TableName tableName) throws IOException {
423   }
424 
425   @Override
426   public void preMove(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo region,
427       ServerName srcServer, ServerName destServer) throws IOException {
428   }
429 
430   @Override
431   public void postMove(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo region,
432       ServerName srcServer, ServerName destServer) throws IOException {
433   }
434 
435   @Override
436   public void preAssign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo)
437       throws IOException {
438   }
439 
440   @Override
441   public void postAssign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo)
442       throws IOException {
443   }
444 
445   @Override
446   public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> ctx,
447       HRegionInfo regionInfo, boolean force) throws IOException {
448   }
449 
450   @Override
451   public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> ctx,
452       HRegionInfo regionInfo, boolean force) throws IOException {
453   }
454 
455   @Override
456   public void preRegionOffline(ObserverContext<MasterCoprocessorEnvironment> ctx,
457       HRegionInfo regionInfo) throws IOException {
458   }
459 
460   @Override
461   public void postRegionOffline(ObserverContext<MasterCoprocessorEnvironment> ctx,
462       HRegionInfo regionInfo) throws IOException {
463   }
464 
465   @Override
466   public void preBalance(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
467   }
468 
469   @Override
470   public void postBalance(ObserverContext<MasterCoprocessorEnvironment> ctx, List<RegionPlan> plans)
471       throws IOException {
472   }
473 
474   @Override
475   public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> ctx,
476       boolean newValue) throws IOException {
477     return false;
478   }
479 
480   @Override
481   public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> ctx,
482       boolean oldValue, boolean newValue) throws IOException {
483   }
484 
485   @Override
486   public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
487   }
488 
489   @Override
490   public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
491   }
492 
493   @Override
494   public void preSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
495       SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException {
496   }
497 
498   @Override
499   public void postSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
500       SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException {
501   }
502 
503   @Override
504   public void preCloneSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
505       SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException {
506   }
507 
508   @Override
509   public void postCloneSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
510       SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException {
511   }
512 
513   @Override
514   public void preRestoreSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
515       SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException {
516   }
517 
518   @Override
519   public void postRestoreSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
520       SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException {
521   }
522 
523   @Override
524   public void preDeleteSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
525       SnapshotDescription snapshot) throws IOException {
526   }
527 
528   @Override
529   public void postDeleteSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
530       SnapshotDescription snapshot) throws IOException {
531   }
532 
533   @Override
534   public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
535       List<TableName> tableNamesList, List<HTableDescriptor> descriptors) throws IOException {
536   }
537 
538   @Override
539   public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx,
540       List<HTableDescriptor> descriptors) throws IOException {
541   }
542 
543   @Override
544   public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
545       NamespaceDescriptor ns) throws IOException {
546   }
547 
548   @Override
549   public void postCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
550       NamespaceDescriptor ns) throws IOException {
551   }
552 
553   @Override
554   public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, 
555       String namespace) throws IOException {
556   }
557 
558   @Override
559   public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
560       String namespace) throws IOException {
561   }
562 
563   @Override
564   public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
565       NamespaceDescriptor ns) throws IOException {
566   }
567 
568   @Override
569   public void postModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
570       NamespaceDescriptor ns) throws IOException {
571   }
572 
573   @Override
574   public void preMasterInitialization(ObserverContext<MasterCoprocessorEnvironment> ctx)
575       throws IOException {
576 
577   }
578 
579   /****************************** Region related hooks ******************************/
580 
581   @Override
582   public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
583     // Read the entire labels table and populate the zk
584     if (e.getEnvironment().getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
585       this.labelsRegion = true;
586       this.acOn = CoprocessorHost.getLoadedCoprocessors().contains(AccessController.class.getName());
587       if (!e.getEnvironment().getRegion().isRecovering()) {
588         initialize(e);
589       }
590     } else {
591       this.initialized = true;
592     }
593   }
594 
595   @Override
596   public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> e) {
597     if (this.labelsRegion) {
598       initialize(e);
599     }
600   }
601 
602   private void initialize(ObserverContext<RegionCoprocessorEnvironment> e) {
603     try {
604       Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths = 
605           extractLabelsAndAuths(getExistingLabelsWithAuths());
606       Map<String, Integer> labels = labelsAndUserAuths.getFirst();
607       Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
608       // Add the "system" label if it is not added into the system yet
609       addSystemLabel(e.getEnvironment().getRegion(), labels, userAuths);
610       int ordinal = 1; // Ordinal 1 is reserved for "system" label.
611       for (Integer i : labels.values()) {
612         if (i > ordinal) {
613           ordinal = i;
614         }
615       }
616       this.ordinalCounter = ordinal + 1;
617       if (labels.size() > 0) {
618         // If there is no data need not write to zk
619         byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(labels);
620         this.visibilityManager.writeToZookeeper(serialized, true);
621       }
622       if (userAuths.size() > 0) {
623         byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
624         this.visibilityManager.writeToZookeeper(serialized, false);
625       }
626       initialized = true;
627     } catch (IOException ioe) {
628       LOG.error("Error while updating the zk with the exisiting labels data", ioe);
629     }
630   }
631 
632   private void addSystemLabel(HRegion region, Map<String, Integer> labels,
633       Map<String, List<Integer>> userAuths) throws IOException {
634     if (!labels.containsKey(SYSTEM_LABEL)) {
635       Put p = new Put(Bytes.toBytes(SYSTEM_LABEL_ORDINAL));
636       p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, Bytes.toBytes(SYSTEM_LABEL));
637       // Set auth for "system" label for all super users.
638       for (String superUser : this.superUsers) {
639         p.addImmutable(
640             LABELS_TABLE_FAMILY, Bytes.toBytes(superUser), DUMMY_VALUE, LABELS_TABLE_TAGS);
641       }
642       region.put(p);
643       labels.put(SYSTEM_LABEL, SYSTEM_LABEL_ORDINAL);
644       for (String superUser : superUsers) {
645         List<Integer> auths = userAuths.get(superUser);
646         if (auths == null) {
647           auths = new ArrayList<Integer>(1);
648           userAuths.put(superUser, auths);
649         }
650         auths.add(SYSTEM_LABEL_ORDINAL);
651       }
652     }
653   }
654 
655   @Override
656   public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
657       MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
658     if (c.getEnvironment().getRegion().getRegionInfo().getTable().isSystemTable()) {
659       return;
660     }
661     // TODO this can be made as a global LRU cache at HRS level?
662     Map<String, List<Tag>> labelCache = new HashMap<String, List<Tag>>();
663     for (int i = 0; i < miniBatchOp.size(); i++) {
664       Mutation m = miniBatchOp.getOperation(i);
665       CellVisibility cellVisibility = null;
666       try {
667         cellVisibility = m.getCellVisibility();
668       } catch (DeserializationException de) {
669         miniBatchOp.setOperationStatus(i,
670             new OperationStatus(SANITY_CHECK_FAILURE, de.getMessage()));
671         continue;
672       }
673         boolean sanityFailure = false;
674         for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
675           if (!checkForReservedVisibilityTagPresence(cellScanner.current())) {
676             miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE,
677                 "Mutation contains cell with reserved type tag"));
678             sanityFailure = true;
679             break;
680           }
681         }
682         if (!sanityFailure) {
683           if (cellVisibility != null) {
684             String labelsExp = cellVisibility.getExpression();
685             List<Tag> visibilityTags = labelCache.get(labelsExp);
686             if (visibilityTags == null) {
687               try {
688                 visibilityTags = createVisibilityTags(labelsExp, true);
689               } catch (ParseException e) {
690                 miniBatchOp.setOperationStatus(i,
691                     new OperationStatus(SANITY_CHECK_FAILURE, e.getMessage()));
692               } catch (InvalidLabelException e) {
693                 miniBatchOp.setOperationStatus(i,
694                     new OperationStatus(SANITY_CHECK_FAILURE, e.getMessage()));
695               }
696             }
697             if (visibilityTags != null) {
698               labelCache.put(labelsExp, visibilityTags);
699               List<Cell> updatedCells = new ArrayList<Cell>();
700               for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) {
701                 Cell cell = cellScanner.current();
702                 List<Tag> tags = Tag.asList(cell.getTagsArray(), cell.getTagsOffset(),
703                     cell.getTagsLengthUnsigned());
704                 tags.addAll(visibilityTags);
705                 Cell updatedCell = new KeyValue(cell.getRowArray(), cell.getRowOffset(),
706                     cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(),
707                     cell.getFamilyLength(), cell.getQualifierArray(), cell.getQualifierOffset(),
708                     cell.getQualifierLength(), cell.getTimestamp(), Type.codeToType(cell
709                         .getTypeByte()), cell.getValueArray(), cell.getValueOffset(),
710                     cell.getValueLength(), tags);
711                 updatedCells.add(updatedCell);
712               }
713             m.getFamilyCellMap().clear();
714             // Clear and add new Cells to the Mutation.
715             for (Cell cell : updatedCells) {
716               if (m instanceof Put) {
717                 Put p = (Put) m;
718                 p.add(cell);
719               } else if (m instanceof Delete) {
720                 // TODO : Cells without visibility tags would be handled in follow up issue
721                 Delete d = (Delete) m;
722                 d.addDeleteMarker(cell);
723               }
724             }
725           }
726         }
727       }
728     }
729   }
730 
731   @Override
732   public void prePrepareTimeStampForDeleteVersion(
733       ObserverContext<RegionCoprocessorEnvironment> ctx, Mutation delete, Cell cell,
734       byte[] byteNow, Get get) throws IOException {
735     KeyValue kv = KeyValueUtil.ensureKeyValue(cell);
736     CellVisibility cellVisibility = null;
737     try {
738       cellVisibility = delete.getCellVisibility();
739     } catch (DeserializationException de) {
740       throw new IOException("Invalid cell visibility specified " + delete, de);
741     }
742     // The check for checkForReservedVisibilityTagPresence happens in preBatchMutate happens.
743     // It happens for every mutation and that would be enough.
744     List<Tag> visibilityTags = new ArrayList<Tag>();
745     if (cellVisibility != null) {
746       String labelsExp = cellVisibility.getExpression();
747       try {
748         visibilityTags = createVisibilityTags(labelsExp, false);
749       } catch (ParseException e) {
750         throw new IOException("Invalid cell visibility expression " + labelsExp, e);
751       } catch (InvalidLabelException e) {
752         throw new IOException("Invalid cell visibility specified " + labelsExp, e);
753       }
754     }
755     get.setFilter(new DeleteVersionVisibilityExpressionFilter(visibilityTags));
756     List<Cell> result = ctx.getEnvironment().getRegion().get(get, false);
757 
758     if (result.size() < get.getMaxVersions()) {
759       // Nothing to delete
760       kv.updateLatestStamp(Bytes.toBytes(Long.MIN_VALUE));
761       return;
762     }
763     if (result.size() > get.getMaxVersions()) {
764       throw new RuntimeException("Unexpected size: " + result.size()
765           + ". Results more than the max versions obtained.");
766     }
767     KeyValue getkv = KeyValueUtil.ensureKeyValue(result.get(get.getMaxVersions() - 1));
768     Bytes.putBytes(kv.getBuffer(), kv.getTimestampOffset(), getkv.getBuffer(),
769         getkv.getTimestampOffset(), Bytes.SIZEOF_LONG);
770     // We are bypassing here because in the HRegion.updateDeleteLatestVersionTimeStamp we would
771     // update with the current timestamp after again doing a get. As the hook as already determined
772     // the needed timestamp we need to bypass here.
773     // TODO : See if HRegion.updateDeleteLatestVersionTimeStamp() could be
774     // called only if the hook is not called.
775     ctx.bypass();
776   }
777 
778   @Override
779   public void postBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
780       MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
781     if (this.labelsRegion) {
782       // We will add to zookeeper here.
783       Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
784           extractLabelsAndAuths(getExistingLabelsWithAuths());
785       Map<String, Integer> existingLabels = labelsAndUserAuths.getFirst();
786       Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
787       boolean isNewLabels = false;
788       boolean isUserAuthsChange = false;
789       for (int i = 0; i < miniBatchOp.size(); i++) {
790         Mutation m = miniBatchOp.getOperation(i);
791         if (miniBatchOp.getOperationStatus(i).getOperationStatusCode() == SUCCESS) {
792           for (List<Cell> cells : m.getFamilyCellMap().values()) {
793             for (Cell cell : cells) {
794               int labelOrdinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset());
795               if (Bytes.equals(cell.getQualifierArray(), cell.getQualifierOffset(),
796                       cell.getQualifierLength(), LABEL_QUALIFIER, 0,
797                       LABEL_QUALIFIER.length)) {
798                 if (m instanceof Put) {
799                   existingLabels.put(
800                       Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
801                           cell.getValueLength()), labelOrdinal);
802                   isNewLabels = true;
803                 }
804               } else {
805                 String user = Bytes.toString(cell.getQualifierArray(),
806                     cell.getQualifierOffset(), cell.getQualifierLength());
807                 List<Integer> auths = userAuths.get(user);
808                 if (auths == null) {
809                   auths = new ArrayList<Integer>();
810                   userAuths.put(user, auths);
811                 }
812                 if (m instanceof Delete) {
813                   auths.remove(Integer.valueOf(labelOrdinal));
814                 } else {
815                   auths.add(labelOrdinal);
816                 }
817                 isUserAuthsChange = true;
818               }
819             }
820           }
821         }
822       }
823       if (isNewLabels) {
824         byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(existingLabels);
825         this.visibilityManager.writeToZookeeper(serialized, true);
826       }
827       if (isUserAuthsChange) {
828         byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
829         this.visibilityManager.writeToZookeeper(serialized, false);
830       }
831     }
832   }
833 
834   private Pair<Map<String, Integer>, Map<String, List<Integer>>> extractLabelsAndAuths(
835       List<List<Cell>> labelDetails) {
836     Map<String, Integer> labels = new HashMap<String, Integer>();
837     Map<String, List<Integer>> userAuths = new HashMap<String, List<Integer>>();
838     for (List<Cell> cells : labelDetails) {
839       for (Cell cell : cells) {
840         if (Bytes.equals(cell.getQualifierArray(), cell.getQualifierOffset(),
841             cell.getQualifierLength(), LABEL_QUALIFIER, 0, LABEL_QUALIFIER.length)) {
842           labels.put(
843               Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()),
844               Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
845         } else {
846           // These are user cells who has authorization for this label
847           String user = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
848               cell.getQualifierLength());
849           List<Integer> auths = userAuths.get(user);
850           if (auths == null) {
851             auths = new ArrayList<Integer>();
852             userAuths.put(user, auths);
853           }
854           auths.add(Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
855         }
856       }
857     }
858     return new Pair<Map<String, Integer>, Map<String, List<Integer>>>(labels, userAuths);
859   }
860 
861   // Checks whether cell contains any tag with type as VISIBILITY_TAG_TYPE.
862   // This tag type is reserved and should not be explicitly set by user.
863   private boolean checkForReservedVisibilityTagPresence(Cell cell) throws IOException {
864     // Bypass this check when the operation is done by a system/super user.
865     // This is done because, while Replication, the Cells coming to the peer cluster with reserved
866     // typed tags and this is fine and should get added to the peer cluster table
867     if (isSystemOrSuperUser()) {
868       return true;
869     }
870     if (cell.getTagsLengthUnsigned() > 0) {
871       Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
872           cell.getTagsLengthUnsigned());
873       while (tagsItr.hasNext()) {
874         if (reservedVisTagTypes.contains(tagsItr.next().getType())) {
875           return false;
876         }
877       }
878     }
879     return true;
880   }
881 
882   private List<Tag> createVisibilityTags(String visibilityLabelsExp, boolean addSerializationTag)
883       throws IOException, ParseException, InvalidLabelException {
884     ExpressionNode node = null;
885     node = this.expressionParser.parse(visibilityLabelsExp);
886     node = this.expressionExpander.expand(node);
887     List<Tag> tags = new ArrayList<Tag>();
888     ByteArrayOutputStream baos = new ByteArrayOutputStream();
889     DataOutputStream dos = new DataOutputStream(baos);
890     List<Integer> labelOrdinals = new ArrayList<Integer>();
891     // We will be adding this tag before the visibility tags and the presence of this
892     // tag indicates we are supporting deletes with cell visibility
893     if (addSerializationTag) {
894       tags.add(VisibilityUtils.VIS_SERIALIZATION_TAG);
895     }
896     if (node.isSingleNode()) {
897       getLabelOrdinals(node, labelOrdinals);
898       writeLabelOrdinalsToStream(labelOrdinals, dos);
899       tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray()));
900       baos.reset();
901     } else {
902       NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
903       if (nlNode.getOperator() == Operator.OR) {
904         for (ExpressionNode child : nlNode.getChildExps()) {
905           getLabelOrdinals(child, labelOrdinals);
906           writeLabelOrdinalsToStream(labelOrdinals, dos);
907           tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray()));
908           baos.reset();
909           labelOrdinals.clear();
910         }
911       } else {
912         getLabelOrdinals(nlNode, labelOrdinals);
913         writeLabelOrdinalsToStream(labelOrdinals, dos);
914         tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray()));
915         baos.reset();
916       }
917     }
918     return tags;
919   }
920 
921   private void writeLabelOrdinalsToStream(List<Integer> labelOrdinals, DataOutputStream dos)
922       throws IOException {
923     Collections.sort(labelOrdinals);
924     for (Integer labelOrdinal : labelOrdinals) {
925       StreamUtils.writeRawVInt32(dos, labelOrdinal);
926     }
927   }
928 
929   private void getLabelOrdinals(ExpressionNode node, List<Integer> labelOrdinals)
930       throws IOException, InvalidLabelException {
931     if (node.isSingleNode()) {
932       String identifier = null;
933       int labelOrdinal = 0;
934       if (node instanceof LeafExpressionNode) {
935         identifier = ((LeafExpressionNode) node)
936             .getIdentifier();
937         if (LOG.isTraceEnabled()) {
938           LOG.trace("The identifier is "+identifier);
939         }
940         labelOrdinal = this.visibilityManager.getLabelOrdinal(identifier);
941       } else {
942         // This is a NOT node.
943         LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node)
944             .getChildExps().get(0);
945         identifier = lNode.getIdentifier();
946         labelOrdinal = this.visibilityManager.getLabelOrdinal(identifier);
947         labelOrdinal = -1 * labelOrdinal; // Store NOT node as -ve ordinal.
948       }
949       if (labelOrdinal == 0) {
950         throw new InvalidLabelException("Invalid visibility label " + identifier);
951       }
952       labelOrdinals.add(labelOrdinal);
953     } else {
954       List<ExpressionNode> childExps = ((NonLeafExpressionNode) node).getChildExps();
955       for (ExpressionNode child : childExps) {
956         getLabelOrdinals(child, labelOrdinals);
957       }
958     }
959   }
960   
961   @Override
962   public RegionScanner preScannerOpen(ObserverContext<RegionCoprocessorEnvironment> e, Scan scan,
963       RegionScanner s) throws IOException {
964     HRegion region = e.getEnvironment().getRegion();
965     Authorizations authorizations = null;
966     // If a super user issues a scan, he should be able to scan the cells
967     // irrespective of the Visibility labels
968     if (checkIfScanOrGetFromSuperUser()) {
969       return s;
970     }
971     try {
972       authorizations = scan.getAuthorizations();
973     } catch (DeserializationException de) {
974       throw new IOException(de);
975     }
976     Filter visibilityLabelFilter = createVisibilityLabelFilter(region, authorizations);
977     if (visibilityLabelFilter != null) {
978       Filter filter = scan.getFilter();
979       if (filter != null) {
980         scan.setFilter(new FilterList(filter, visibilityLabelFilter));
981       } else {
982         scan.setFilter(visibilityLabelFilter);
983       }
984     }
985     return s;
986   }
987 
988   private boolean checkIfScanOrGetFromSuperUser() throws IOException {
989     User user = getActiveUser();
990     if (user != null && user.getShortName() != null) {
991       List<String> auths = this.visibilityManager.getAuths(user.getShortName());
992       return (auths.contains(SYSTEM_LABEL));
993     }
994     return false;
995   }
996 
997   @Override
998   public DeleteTracker postInstantiateDeleteTracker(
999       ObserverContext<RegionCoprocessorEnvironment> ctx, DeleteTracker delTracker)
1000       throws IOException {
1001     HRegion region = ctx.getEnvironment().getRegion();
1002     TableName table = region.getRegionInfo().getTable();
1003     if (table.isSystemTable()) {
1004       return delTracker;
1005     }
1006     // We are creating a new type of delete tracker here which is able to track
1007     // the timestamps and also the
1008     // visibility tags per cell. The covering cells are determined not only
1009     // based on the delete type and ts
1010     // but also on the visibility expression matching.
1011     return new VisibilityScanDeleteTracker();
1012   }
1013   @Override
1014   public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
1015       final Scan scan, final RegionScanner s) throws IOException {
1016     User user = getActiveUser();
1017     if (user != null && user.getShortName() != null) {
1018       scannerOwners.put(s, user.getShortName());
1019     }
1020     return s;
1021   }
1022 
1023   @Override
1024   public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
1025       final InternalScanner s, final List<Result> result, final int limit, final boolean hasNext)
1026       throws IOException {
1027     requireScannerOwner(s);
1028     return hasNext;
1029   }
1030 
1031   @Override
1032   public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1033       final InternalScanner s) throws IOException {
1034     requireScannerOwner(s);
1035   }
1036 
1037   @Override
1038   public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
1039       final InternalScanner s) throws IOException {
1040     // clean up any associated owner mapping
1041     scannerOwners.remove(s);
1042   }
1043 
1044   /**
1045    * Verify, when servicing an RPC, that the caller is the scanner owner. If so, we assume that
1046    * access control is correctly enforced based on the checks performed in preScannerOpen()
1047    */
1048   private void requireScannerOwner(InternalScanner s) throws AccessDeniedException {
1049     if (RequestContext.isInRequestContext()) {
1050       String requestUName = RequestContext.getRequestUserName();
1051       String owner = scannerOwners.get(s);
1052       if (owner != null && !owner.equals(requestUName)) {
1053         throw new AccessDeniedException("User '" + requestUName + "' is not the scanner owner!");
1054       }
1055     }
1056   }
1057 
1058   @Override
1059   public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results)
1060       throws IOException {
1061     Authorizations authorizations = null;
1062     // If a super user issues a get, he should be able to scan the cells
1063     // irrespective of the Visibility labels
1064     if (checkIfScanOrGetFromSuperUser()) {
1065       return;
1066     }
1067     try {
1068       authorizations = get.getAuthorizations();
1069     } catch (DeserializationException de) {
1070       throw new IOException(de);
1071     }
1072     Filter visibilityLabelFilter = createVisibilityLabelFilter(e.getEnvironment().getRegion(),
1073         authorizations);
1074     if (visibilityLabelFilter != null) {
1075       Filter filter = get.getFilter();
1076       if (filter != null) {
1077         get.setFilter(new FilterList(filter, visibilityLabelFilter));
1078       } else {
1079         get.setFilter(visibilityLabelFilter);
1080       }
1081     }
1082   }
1083 
1084   private Filter createVisibilityLabelFilter(HRegion region, Authorizations authorizations)
1085       throws IOException {
1086     Map<ByteRange, Integer> cfVsMaxVersions = new HashMap<ByteRange, Integer>();
1087     for (HColumnDescriptor hcd : region.getTableDesc().getFamilies()) {
1088       cfVsMaxVersions.put(new SimpleByteRange(hcd.getName()), hcd.getMaxVersions());
1089     }
1090     if (authorizations == null) {
1091       // No Authorizations present for this scan/Get!
1092       // In case of system tables other than "labels" just scan with out visibility check and
1093       // filtering. Checking visibility labels for META and NAMESPACE table is not needed.
1094       TableName table = region.getRegionInfo().getTable();
1095       if (table.isSystemTable() && !table.equals(LABELS_TABLE_NAME)) {
1096         return null;
1097       }
1098     } else {
1099       for (String label : authorizations.getLabels()) {
1100         if (!VisibilityLabelsValidator.isValidLabel(label)) {
1101           throw new IllegalArgumentException("Invalid authorization label : " + label
1102               + ". Authorizations cannot contain '(', ')' ,'&' ,'|', '!'" + " and cannot be empty");
1103         }
1104       }
1105     }
1106     Filter visibilityLabelFilter = null;
1107     if (this.scanLabelGenerators != null) {
1108       List<String> labels = null;
1109       for (ScanLabelGenerator scanLabelGenerator : this.scanLabelGenerators) {
1110         try {
1111           // null authorizations to be handled inside SLG impl.
1112           labels = scanLabelGenerator.getLabels(getActiveUser(), authorizations);
1113           labels = (labels == null) ? new ArrayList<String>() : labels;
1114           authorizations = new Authorizations(labels);
1115         } catch (Throwable t) {
1116           LOG.error(t);
1117           throw new IOException(t);
1118         }
1119       }
1120       int labelsCount = this.visibilityManager.getLabelsCount();
1121       BitSet bs = new BitSet(labelsCount + 1); // ordinal is index 1 based
1122       if (labels != null) {
1123         for (String label : labels) {
1124           int labelOrdinal = this.visibilityManager.getLabelOrdinal(label);
1125           if (labelOrdinal != 0) {
1126             bs.set(labelOrdinal);
1127           }
1128         }
1129       }
1130       visibilityLabelFilter = new VisibilityLabelFilter(bs, cfVsMaxVersions);
1131     }
1132     return visibilityLabelFilter;
1133   }
1134 
1135   private User getActiveUser() throws IOException {
1136     User user = RequestContext.getRequestUser();
1137     if (!RequestContext.isInRequestContext()) {
1138       // for non-rpc handling, fallback to system user
1139       user = User.getCurrent();
1140     }
1141     if (LOG.isTraceEnabled()) {
1142       LOG.trace("Current active user name is "+user.getShortName());
1143     }
1144     return user;
1145   }
1146 
1147   private List<String> getSystemAndSuperUsers() throws IOException {
1148     User user = User.getCurrent();
1149     if (user == null) {
1150       throw new IOException("Unable to obtain the current user, "
1151           + "authorization checks for internal operations will not work correctly!");
1152     }
1153     if (LOG.isTraceEnabled()) {
1154       LOG.trace("Current user name is "+user.getShortName());
1155     }
1156     String currentUser = user.getShortName();
1157     List<String> superUsers = Lists.asList(currentUser,
1158         this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
1159     return superUsers;
1160   }
1161 
1162   private boolean isSystemOrSuperUser() throws IOException {
1163     User activeUser = getActiveUser();
1164     return this.superUsers.contains(activeUser.getShortName());
1165   }
1166 
1167   @Override
1168   public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> e, Append append)
1169       throws IOException {
1170     for (CellScanner cellScanner = append.cellScanner(); cellScanner.advance();) {
1171       if (!checkForReservedVisibilityTagPresence(cellScanner.current())) {
1172         throw new FailedSanityCheckException("Append contains cell with reserved type tag");
1173       }
1174     }
1175     return null;
1176   }
1177 
1178   @Override
1179   public Result preIncrement(ObserverContext<RegionCoprocessorEnvironment> e, Increment increment)
1180       throws IOException {
1181     for (CellScanner cellScanner = increment.cellScanner(); cellScanner.advance();) {
1182       if (!checkForReservedVisibilityTagPresence(cellScanner.current())) {
1183         throw new FailedSanityCheckException("Increment contains cell with reserved type tag");
1184       }
1185     }
1186     return null;
1187   }
1188 
1189   @Override
1190   public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx,
1191       MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException {
1192     List<Tag> tags = Lists.newArrayList();
1193     CellVisibility cellVisibility = null;
1194     try {
1195       cellVisibility = mutation.getCellVisibility();
1196     } catch (DeserializationException e) {
1197       throw new IOException(e);
1198     }
1199     if (cellVisibility == null) {
1200       return newCell;
1201     }
1202     // Adding all other tags
1203     Iterator<Tag> tagsItr = CellUtil.tagsIterator(newCell.getTagsArray(), newCell.getTagsOffset(),
1204         newCell.getTagsLengthUnsigned());
1205     while (tagsItr.hasNext()) {
1206       Tag tag = tagsItr.next();
1207       if (tag.getType() != VisibilityUtils.VISIBILITY_TAG_TYPE
1208           && tag.getType() != VisibilityUtils.VISIBILITY_EXP_SERIALIZATION_TAG_TYPE) {
1209         tags.add(tag);
1210       }
1211     }
1212     try {
1213       tags.addAll(createVisibilityTags(cellVisibility.getExpression(), true));
1214     } catch (ParseException e) {
1215       throw new IOException(e);
1216     }
1217 
1218     // We need to create another KV, unfortunately, because the current new KV
1219     // has no space for tags
1220     KeyValue newKv = KeyValueUtil.ensureKeyValue(newCell);
1221     byte[] bytes = newKv.getBuffer();
1222     KeyValue rewriteKv = new KeyValue(bytes, newKv.getRowOffset(), newKv.getRowLength(), bytes,
1223         newKv.getFamilyOffset(), newKv.getFamilyLength(), bytes, newKv.getQualifierOffset(),
1224         newKv.getQualifierLength(), newKv.getTimestamp(), KeyValue.Type.codeToType(newKv
1225             .getTypeByte()), bytes, newKv.getValueOffset(), newKv.getValueLength(), tags);
1226     // Preserve mvcc data
1227     rewriteKv.setMvccVersion(newKv.getMvccVersion());
1228     return rewriteKv;
1229   }
1230 
1231   @Override
1232   public Service getService() {
1233     return VisibilityLabelsProtos.VisibilityLabelsService.newReflectiveService(this);
1234   }
1235 
1236   /****************************** VisibilityEndpoint service related methods ******************************/
1237   @Override
1238   public synchronized void addLabels(RpcController controller, VisibilityLabelsRequest request,
1239       RpcCallback<VisibilityLabelsResponse> done) {
1240     VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
1241     List<VisibilityLabel> labels = request.getVisLabelList();
1242     if (!initialized) {
1243       setExceptionResults(labels.size(), new CoprocessorException(
1244           "VisibilityController not yet initialized"), response);
1245     }
1246     try {
1247       checkCallingUserAuth();
1248       List<Mutation> puts = new ArrayList<Mutation>(labels.size());
1249       RegionActionResult successResult = RegionActionResult.newBuilder().build();
1250       for (VisibilityLabel visLabel : labels) {
1251         byte[] label = visLabel.getLabel().toByteArray();
1252         String labelStr = Bytes.toString(label);
1253         if (VisibilityLabelsValidator.isValidLabel(label)) {
1254           if (this.visibilityManager.getLabelOrdinal(labelStr) > 0) {
1255             RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1256             failureResultBuilder.setException(ResponseConverter
1257                 .buildException(new LabelAlreadyExistsException("Label '" + labelStr
1258                     + "' already exists")));
1259             response.addResult(failureResultBuilder.build());
1260           } else {
1261             Put p = new Put(Bytes.toBytes(ordinalCounter));
1262             p.addImmutable(
1263                 LABELS_TABLE_FAMILY, LABEL_QUALIFIER, label, LABELS_TABLE_TAGS);
1264             if (LOG.isDebugEnabled()) {
1265               LOG.debug("Adding the label "+labelStr);
1266             }
1267             puts.add(p);
1268             ordinalCounter++;
1269             response.addResult(successResult);
1270           }
1271         } else {
1272           RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1273           failureResultBuilder.setException(ResponseConverter
1274               .buildException(new InvalidLabelException("Invalid visibility label '" + labelStr
1275                   + "'")));
1276           response.addResult(failureResultBuilder.build());
1277         }
1278       }
1279       OperationStatus[] opStatus = this.regionEnv.getRegion().batchMutate(
1280           puts.toArray(new Mutation[puts.size()]));
1281       int i = 0;
1282       for (OperationStatus status : opStatus) {
1283         if (status.getOperationStatusCode() != SUCCESS) {
1284           while (response.getResult(i) != successResult)
1285             i++;
1286           RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1287           failureResultBuilder.setException(ResponseConverter
1288               .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
1289           response.setResult(i, failureResultBuilder.build());
1290         }
1291         i++;
1292       }
1293     } catch (IOException e) {
1294       LOG.error(e);
1295       setExceptionResults(labels.size(), e, response);
1296     }
1297     done.run(response.build());
1298   }
1299 
1300   private void setExceptionResults(int size, IOException e,
1301       VisibilityLabelsResponse.Builder response) {
1302     RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1303     failureResultBuilder.setException(ResponseConverter.buildException(e));
1304     RegionActionResult failureResult = failureResultBuilder.build();
1305     for (int i = 0; i < size; i++) {
1306       response.addResult(i, failureResult);
1307     }
1308   }
1309 
1310   private void performACLCheck() throws IOException {
1311     // Do ACL check only when the security is enabled.
1312     if (this.acOn && !isSystemOrSuperUser()) {
1313       User user = getActiveUser();
1314       throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null")
1315           + " is not authorized to perform this action.");
1316     }
1317   }
1318 
1319   private List<List<Cell>> getExistingLabelsWithAuths() throws IOException {
1320     Scan scan = new Scan();
1321     RegionScanner scanner = this.regionEnv.getRegion().getScanner(scan);
1322     List<List<Cell>> existingLabels = new ArrayList<List<Cell>>();
1323     try {
1324       while (true) {
1325         List<Cell> cells = new ArrayList<Cell>();
1326         scanner.next(cells);
1327         if (cells.isEmpty()) {
1328           break;
1329         }
1330         existingLabels.add(cells);
1331       }
1332     } finally {
1333       scanner.close();
1334     }
1335     return existingLabels;
1336   }
1337 
1338   @Override
1339   public synchronized void setAuths(RpcController controller, SetAuthsRequest request,
1340       RpcCallback<VisibilityLabelsResponse> done) {
1341     VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
1342     List<ByteString> auths = request.getAuthList();
1343     if (!initialized) {
1344       setExceptionResults(auths.size(), new CoprocessorException(
1345           "VisibilityController not yet initialized"), response);
1346     }
1347     byte[] user = request.getUser().toByteArray();
1348     try {
1349       checkCallingUserAuth();
1350       List<Mutation> puts = new ArrayList<Mutation>(auths.size());
1351       RegionActionResult successResult = RegionActionResult.newBuilder().build();
1352       for (ByteString authBS : auths) {
1353         byte[] auth = authBS.toByteArray();
1354         String authStr = Bytes.toString(auth);
1355         int labelOrdinal = this.visibilityManager.getLabelOrdinal(authStr);
1356         if (labelOrdinal == 0) {
1357           // This label is not yet added. 1st this should be added to the system
1358           RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1359           failureResultBuilder.setException(ResponseConverter
1360               .buildException(new InvalidLabelException("Label '" + authStr + "' doesn't exist")));
1361           response.addResult(failureResultBuilder.build());
1362         } else {
1363           Put p = new Put(Bytes.toBytes(labelOrdinal));
1364           p.addImmutable(
1365               LABELS_TABLE_FAMILY, user, DUMMY_VALUE, LABELS_TABLE_TAGS);
1366           puts.add(p);
1367           response.addResult(successResult);
1368         }
1369       }
1370       OperationStatus[] opStatus = this.regionEnv.getRegion().batchMutate(
1371           puts.toArray(new Mutation[puts.size()]));
1372       int i = 0;
1373       for (OperationStatus status : opStatus) {
1374         if (status.getOperationStatusCode() != SUCCESS) {
1375           while (response.getResult(i) != successResult) i++;
1376           RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1377           failureResultBuilder.setException(ResponseConverter
1378               .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
1379           response.setResult(i, failureResultBuilder.build());
1380         }
1381         i++;
1382       }
1383     } catch (IOException e) {
1384       LOG.error(e);
1385       setExceptionResults(auths.size(), e, response);
1386     }
1387     done.run(response.build());
1388   }
1389 
1390   @Override
1391   public synchronized void getAuths(RpcController controller, GetAuthsRequest request,
1392       RpcCallback<GetAuthsResponse> done) {
1393     byte[] user = request.getUser().toByteArray();
1394     GetAuthsResponse.Builder response = GetAuthsResponse.newBuilder();
1395     response.setUser(request.getUser());
1396     try {
1397       List<String> labels = getUserAuthsFromLabelsTable(user);
1398       for (String label : labels) {
1399         response.addAuth(ByteStringer.wrap(Bytes.toBytes(label)));
1400       }
1401     } catch (IOException e) {
1402       ResponseConverter.setControllerException(controller, e);
1403     }
1404     done.run(response.build());
1405   }
1406 
1407   private List<String> getUserAuthsFromLabelsTable(byte[] user) throws IOException {
1408     Scan s = new Scan();
1409     s.addColumn(LABELS_TABLE_FAMILY, user);
1410     Filter filter = createVisibilityLabelFilter(this.regionEnv.getRegion(), new Authorizations(
1411         SYSTEM_LABEL));
1412     s.setFilter(filter);
1413     List<String> auths = new ArrayList<String>();
1414     // We do ACL check here as we create scanner directly on region. It will not make calls to
1415     // AccessController CP methods.
1416     performACLCheck();
1417     RegionScanner scanner = this.regionEnv.getRegion().getScanner(s);
1418     List<Cell> results = new ArrayList<Cell>(1);
1419     while (true) {
1420       scanner.next(results);
1421       if (results.isEmpty()) break;
1422       Cell cell = results.get(0);
1423       int ordinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
1424       String label = this.visibilityManager.getLabel(ordinal);
1425       if (label != null) {
1426         auths.add(label);
1427       }
1428       results.clear();
1429     }
1430     return auths;
1431   }
1432 
1433   @Override
1434   public synchronized void clearAuths(RpcController controller, SetAuthsRequest request,
1435       RpcCallback<VisibilityLabelsResponse> done) {
1436     VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder();
1437     List<ByteString> auths = request.getAuthList();
1438     if (!initialized) {
1439       setExceptionResults(auths.size(), new CoprocessorException(
1440           "VisibilityController not yet initialized"), response);
1441     }
1442     byte[] user = request.getUser().toByteArray();
1443     try {
1444       checkCallingUserAuth();
1445       List<String> currentAuths = this.getUserAuthsFromLabelsTable(user);
1446       List<Mutation> deletes = new ArrayList<Mutation>(auths.size());
1447       RegionActionResult successResult = RegionActionResult.newBuilder().build();
1448       for (ByteString authBS : auths) {
1449         byte[] auth = authBS.toByteArray();
1450         String authStr = Bytes.toString(auth);
1451         if (currentAuths.contains(authStr)) {
1452           int labelOrdinal = this.visibilityManager.getLabelOrdinal(authStr);
1453           assert labelOrdinal > 0;
1454           Delete d = new Delete(Bytes.toBytes(labelOrdinal));
1455           d.deleteColumns(LABELS_TABLE_FAMILY, user);
1456           deletes.add(d);
1457           response.addResult(successResult);
1458         } else {
1459           // This label is not set for the user.
1460           RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1461           failureResultBuilder.setException(ResponseConverter
1462               .buildException(new InvalidLabelException("Label '" + authStr
1463                   + "' is not set for the user " + Bytes.toString(user))));
1464           response.addResult(failureResultBuilder.build());
1465         }
1466       }
1467       OperationStatus[] opStatus = this.regionEnv.getRegion().batchMutate(
1468           deletes.toArray(new Mutation[deletes.size()]));
1469       int i = 0;
1470       for (OperationStatus status : opStatus) {
1471         if (status.getOperationStatusCode() != SUCCESS) {
1472           while (response.getResult(i) != successResult) i++;
1473           RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder();
1474           failureResultBuilder.setException(ResponseConverter
1475               .buildException(new DoNotRetryIOException(status.getExceptionMsg())));
1476           response.setResult(i, failureResultBuilder.build());
1477         }
1478         i++;
1479       }
1480     } catch (IOException e) {
1481       LOG.error(e);
1482       setExceptionResults(auths.size(), e, response);
1483     }
1484     done.run(response.build());
1485   }
1486 
1487   private void checkCallingUserAuth() throws IOException {
1488     if (!this.acOn) {
1489       User user = getActiveUser();
1490       if (user == null) {
1491         throw new IOException("Unable to retrieve calling user");
1492       }
1493       List<String> auths = this.visibilityManager.getAuths(user.getShortName());
1494       if (LOG.isTraceEnabled()) {
1495         LOG.trace("The list of auths are "+auths);
1496       }
1497       if (!auths.contains(SYSTEM_LABEL)) {
1498         throw new AccessDeniedException("User '" + user.getShortName()
1499             + "' is not authorized to perform this action.");
1500       }
1501     }
1502   }
1503 
1504   static class DeleteVersionVisibilityExpressionFilter extends FilterBase {
1505     private List<Tag> visibilityTags;
1506 
1507     public DeleteVersionVisibilityExpressionFilter(List<Tag> visibilityTags) {
1508       this.visibilityTags = visibilityTags;
1509     }
1510 
1511     @Override
1512     public ReturnCode filterKeyValue(Cell kv) throws IOException {
1513       boolean matchFound = VisibilityUtils.checkForMatchingVisibilityTags(kv, visibilityTags);
1514       if (matchFound) {
1515         return ReturnCode.INCLUDE;
1516       } else {
1517         return ReturnCode.SKIP;
1518       }
1519     }
1520   }
1521 }