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