Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.security.visibility; import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SANITY_CHECK_FAILURE; import static org.apache.hadoop.hbase.HConstants.OperationStatusCode.SUCCESS; import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY; import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME; import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABEL_QUALIFIER; import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellScanner; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.CoprocessorEnvironment; import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.KeyValue.Type; import org.apache.hadoop.hbase.KeyValueUtil; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Tag; import org.apache.hadoop.hbase.catalog.MetaReader; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.constraint.ConstraintException; import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver; import org.apache.hadoop.hbase.coprocessor.CoprocessorException; import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; import org.apache.hadoop.hbase.coprocessor.CoprocessorService; import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.MasterObserver; import org.apache.hadoop.hbase.coprocessor.ObserverContext; import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.RegionObserver; import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.filter.Filter; import org.apache.hadoop.hbase.filter.FilterList; import org.apache.hadoop.hbase.io.hfile.HFile; import org.apache.hadoop.hbase.io.util.StreamUtils; import org.apache.hadoop.hbase.ipc.RequestContext; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.master.RegionPlan; import org.apache.hadoop.hbase.protobuf.ResponseConverter; import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsRequest; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.SetAuthsRequest; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService; import org.apache.hadoop.hbase.regionserver.BloomType; import org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy; import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.InternalScanner; import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress; import org.apache.hadoop.hbase.regionserver.OperationStatus; import org.apache.hadoop.hbase.regionserver.RegionScanner; import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.access.AccessControlLists; import org.apache.hadoop.hbase.security.access.AccessController; import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode; import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode; import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode; import org.apache.hadoop.hbase.security.visibility.expression.Operator; import org.apache.hadoop.hbase.util.ByteRange; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.hbase.util.SimpleByteRange; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import com.google.protobuf.ByteString; import com.google.protobuf.HBaseZeroCopyByteString; import com.google.protobuf.RpcCallback; import com.google.protobuf.RpcController; import com.google.protobuf.Service; /** * Coprocessor that has both the MasterObserver and RegionObserver implemented that supports in * visibility labels */ @InterfaceAudience.Private public class VisibilityController extends BaseRegionObserver implements MasterObserver, RegionObserver, VisibilityLabelsService.Interface, CoprocessorService { private static final Log LOG = LogFactory.getLog(VisibilityController.class); private static final byte[] DUMMY_VALUE = new byte[0]; // "system" label is having an ordinal value 1. private static final int SYSTEM_LABEL_ORDINAL = 1; private static final Tag[] LABELS_TABLE_TAGS = new Tag[1]; private final ExpressionParser expressionParser = new ExpressionParser(); private final ExpressionExpander expressionExpander = new ExpressionExpander(); private VisibilityLabelsManager visibilityManager; // defined only for Endpoint implementation, so it can have way to access region services. private RegionCoprocessorEnvironment regionEnv; private List<ScanLabelGenerator> scanLabelGenerators; private volatile int ordinalCounter = -1; // flags if we are running on a region of the 'labels' table private boolean labelsRegion = false; // Flag denoting whether AcessController is available or not. private boolean acOn = false; private Configuration conf; private volatile boolean initialized = false; /** Mapping of scanner instances to the user who created them */ private Map<InternalScanner, String> scannerOwners = new MapMaker().weakKeys().makeMap(); static { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); try { StreamUtils.writeRawVInt32(dos, SYSTEM_LABEL_ORDINAL); } catch (IOException e) { // We write to a byte array. No Exception can happen. } LABELS_TABLE_TAGS[0] = new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray()); } @Override public void start(CoprocessorEnvironment env) throws IOException { this.conf = env.getConfiguration(); if (HFile.getFormatVersion(conf) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) { throw new RuntimeException("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS + " is required to persist visibility labels. Consider setting " + HFile.FORMAT_VERSION_KEY + " accordingly."); } ZooKeeperWatcher zk = null; if (env instanceof MasterCoprocessorEnvironment) { // if running on HMaster MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) env; zk = mEnv.getMasterServices().getZooKeeper(); } else if (env instanceof RegionCoprocessorEnvironment) { // if running at region regionEnv = (RegionCoprocessorEnvironment) env; zk = regionEnv.getRegionServerServices().getZooKeeper(); } else if (env instanceof RegionServerCoprocessorEnvironment) { throw new RuntimeException("Visibility controller should not be configured as " + "'hbase.coprocessor.regionserver.classes'."); } // If zk is null or IOException while obtaining auth manager, // throw RuntimeException so that the coprocessor is unloaded. if (zk == null) { throw new RuntimeException("Error obtaining VisibilityLabelsManager, zk found null."); } try { this.visibilityManager = VisibilityLabelsManager.get(zk, this.conf); } catch (IOException ioe) { throw new RuntimeException("Error obtaining VisibilityLabelsManager", ioe); } if (env instanceof RegionCoprocessorEnvironment) { // ScanLabelGenerator to be instantiated only with Region Observer. scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf); } } @Override public void stop(CoprocessorEnvironment env) throws IOException { } /********************************* Master related hooks **********************************/ @Override public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { // Need to create the new system table for labels here MasterServices master = ctx.getEnvironment().getMasterServices(); if (!MetaReader.tableExists(master.getCatalogTracker(), LABELS_TABLE_NAME)) { HTableDescriptor labelsTable = new HTableDescriptor(LABELS_TABLE_NAME); HColumnDescriptor labelsColumn = new HColumnDescriptor(LABELS_TABLE_FAMILY); labelsColumn.setBloomFilterType(BloomType.NONE); labelsColumn.setBlockCacheEnabled(false); // We will cache all the labels. No need of normal // table block cache. labelsTable.addFamily(labelsColumn); // Let the "labels" table having only one region always. We are not expecting too many labels in // the system. labelsTable.setValue(HTableDescriptor.SPLIT_POLICY, DisabledRegionSplitPolicy.class.getName()); labelsTable.setValue(Bytes.toBytes(HConstants.DISALLOW_WRITES_IN_RECOVERING), Bytes.toBytes(true)); master.createTable(labelsTable, null); } } @Override public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, HTableDescriptor desc, HRegionInfo[] regions) throws IOException { } @Override public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, HTableDescriptor desc, HRegionInfo[] regions) throws IOException { } @Override public void preCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, HTableDescriptor desc, HRegionInfo[] regions) throws IOException { } @Override public void postCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, HTableDescriptor desc, HRegionInfo[] regions) throws IOException { } @Override public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HTableDescriptor htd) throws IOException { if (LABELS_TABLE_NAME.equals(tableName)) { throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME); } } @Override public void postModifyTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HTableDescriptor htd) throws IOException { } @Override public void preModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HTableDescriptor htd) throws IOException { } @Override public void postModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HTableDescriptor htd) throws IOException { } @Override public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor column) throws IOException { if (LABELS_TABLE_NAME.equals(tableName)) { throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME); } } @Override public void postAddColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor column) throws IOException { } @Override public void preAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor column) throws IOException { } @Override public void postAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor column) throws IOException { } @Override public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor descriptor) throws IOException { if (LABELS_TABLE_NAME.equals(tableName)) { throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME); } } @Override public void postModifyColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor descriptor) throws IOException { } @Override public void preModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor descriptor) throws IOException { } @Override public void postModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, HColumnDescriptor descriptor) throws IOException { } @Override public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, byte[] c) throws IOException { if (LABELS_TABLE_NAME.equals(tableName)) { throw new ConstraintException("Cannot alter " + LABELS_TABLE_NAME); } } @Override public void postDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, byte[] c) throws IOException { } @Override public void preDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, byte[] c) throws IOException { } @Override public void postDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName, byte[] c) throws IOException { } @Override public void preTruncateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postTruncateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preTruncateTableHandler(final ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postTruncateTableHandler(final ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postEnableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { if (LABELS_TABLE_NAME.equals(tableName)) { throw new ConstraintException("Cannot disable " + LABELS_TABLE_NAME); } } @Override public void postDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preMove(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo region, ServerName srcServer, ServerName destServer) throws IOException { } @Override public void postMove(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo region, ServerName srcServer, ServerName destServer) throws IOException { } @Override public void preAssign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo) throws IOException { } @Override public void postAssign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo) throws IOException { } @Override public void preUnassign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo, boolean force) throws IOException { } @Override public void postUnassign(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo, boolean force) throws IOException { } @Override public void preRegionOffline(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo) throws IOException { } @Override public void postRegionOffline(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo regionInfo) throws IOException { } @Override public void preBalance(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { } @Override public void postBalance(ObserverContext<MasterCoprocessorEnvironment> ctx, List<RegionPlan> plans) throws IOException { } @Override public boolean preBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> ctx, boolean newValue) throws IOException { return false; } @Override public void postBalanceSwitch(ObserverContext<MasterCoprocessorEnvironment> ctx, boolean oldValue, boolean newValue) throws IOException { } @Override public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { } @Override public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { } @Override public void preSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException { } @Override public void postSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException { } @Override public void preCloneSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException { } @Override public void postCloneSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException { } @Override public void preRestoreSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException { } @Override public void postRestoreSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot, HTableDescriptor hTableDescriptor) throws IOException { } @Override public void preDeleteSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot) throws IOException { } @Override public void postDeleteSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx, SnapshotDescription snapshot) throws IOException { } @Override public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx, List<TableName> tableNamesList, List<HTableDescriptor> descriptors) throws IOException { } @Override public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx, List<HTableDescriptor> descriptors) throws IOException { } @Override public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, NamespaceDescriptor ns) throws IOException { } @Override public void postCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, NamespaceDescriptor ns) throws IOException { } @Override public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace) throws IOException { } @Override public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace) throws IOException { } @Override public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, NamespaceDescriptor ns) throws IOException { } @Override public void postModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, NamespaceDescriptor ns) throws IOException { } @Override public void preTableFlush(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void postTableFlush(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { } @Override public void preMasterInitialization(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { } /****************************** Region related hooks ******************************/ @Override public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) { // Read the entire labels table and populate the zk if (e.getEnvironment().getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) { this.labelsRegion = true; this.acOn = CoprocessorHost.getLoadedCoprocessors().contains(AccessController.class.getName()); if (!e.getEnvironment().getRegion().isRecovering()) { initialize(e); } } else { this.initialized = true; } } @Override public void postLogReplay(ObserverContext<RegionCoprocessorEnvironment> e) { if (this.labelsRegion) { initialize(e); } } private void initialize(ObserverContext<RegionCoprocessorEnvironment> e) { try { Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths = extractLabelsAndAuths( getExistingLabelsWithAuths()); Map<String, Integer> labels = labelsAndUserAuths.getFirst(); Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond(); // Add the "system" label if it is not added into the system yet addSystemLabel(e.getEnvironment().getRegion(), labels, userAuths); int ordinal = 1; // Ordinal 1 is reserved for "system" label. for (Integer i : labels.values()) { if (i > ordinal) { ordinal = i; } } this.ordinalCounter = ordinal + 1; if (labels.size() > 0) { // If there is no data need not write to zk byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(labels); this.visibilityManager.writeToZookeeper(serialized, true); } if (userAuths.size() > 0) { byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths); this.visibilityManager.writeToZookeeper(serialized, false); } initialized = true; } catch (IOException ioe) { LOG.error("Error while updating the zk with the exisiting labels data", ioe); } } private void addSystemLabel(HRegion region, Map<String, Integer> labels, Map<String, List<Integer>> userAuths) throws IOException { if (!labels.containsKey(SYSTEM_LABEL)) { Put p = new Put(Bytes.toBytes(SYSTEM_LABEL_ORDINAL)); p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, Bytes.toBytes(SYSTEM_LABEL)); // Set auth for "system" label for all super users. List<String> superUsers = getSystemAndSuperUsers(); for (String superUser : superUsers) { p.addImmutable(LABELS_TABLE_FAMILY, Bytes.toBytes(superUser), DUMMY_VALUE, LABELS_TABLE_TAGS); } region.put(p); labels.put(SYSTEM_LABEL, SYSTEM_LABEL_ORDINAL); for (String superUser : superUsers) { List<Integer> auths = userAuths.get(superUser); if (auths == null) { auths = new ArrayList<Integer>(1); userAuths.put(superUser, auths); } auths.add(SYSTEM_LABEL_ORDINAL); } } } @Override public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c, MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException { if (c.getEnvironment().getRegion().getRegionInfo().getTable().isSystemTable()) { return; } // TODO this can be made as a global LRU cache at HRS level? Map<String, List<Tag>> labelCache = new HashMap<String, List<Tag>>(); for (int i = 0; i < miniBatchOp.size(); i++) { Mutation m = miniBatchOp.getOperation(i); CellVisibility cellVisibility = null; try { cellVisibility = m.getCellVisibility(); } catch (DeserializationException de) { miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE, de.getMessage())); continue; } if (m instanceof Put) { Put p = (Put) m; boolean sanityFailure = false; for (CellScanner cellScanner = p.cellScanner(); cellScanner.advance();) { if (!checkForReservedVisibilityTagPresence(cellScanner.current())) { miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE, "Mutation contains cell with reserved type tag")); sanityFailure = true; break; } } if (!sanityFailure) { if (cellVisibility != null) { String labelsExp = cellVisibility.getExpression(); List<Tag> visibilityTags = labelCache.get(labelsExp); if (visibilityTags == null) { try { visibilityTags = createVisibilityTags(labelsExp); } catch (ParseException e) { miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE, e.getMessage())); } catch (InvalidLabelException e) { miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE, e.getMessage())); } } if (visibilityTags != null) { labelCache.put(labelsExp, visibilityTags); List<Cell> updatedCells = new ArrayList<Cell>(); for (CellScanner cellScanner = p.cellScanner(); cellScanner.advance();) { Cell cell = cellScanner.current(); List<Tag> tags = Tag.asList(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength()); tags.addAll(visibilityTags); Cell updatedCell = new KeyValue(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(), cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(), cell.getTimestamp(), Type.codeToType(cell.getTypeByte()), cell.getValueArray(), cell.getValueOffset(), cell.getValueLength(), tags); updatedCells.add(updatedCell); } p.getFamilyCellMap().clear(); // Clear and add new Cells to the Mutation. for (Cell cell : updatedCells) { p.add(cell); } } } } } else if (cellVisibility != null) { // CellVisibility in a Delete is not legal! Fail the operation miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE, "CellVisibility cannot be set on Delete mutation")); } } } @Override public void postBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c, MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException { if (this.labelsRegion) { // We will add to zookeeper here. Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths = extractLabelsAndAuths( getExistingLabelsWithAuths()); Map<String, Integer> existingLabels = labelsAndUserAuths.getFirst(); Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond(); boolean isNewLabels = false; boolean isUserAuthsChange = false; for (int i = 0; i < miniBatchOp.size(); i++) { Mutation m = miniBatchOp.getOperation(i); if (miniBatchOp.getOperationStatus(i).getOperationStatusCode() == SUCCESS) { for (List<Cell> cells : m.getFamilyCellMap().values()) { for (Cell cell : cells) { int labelOrdinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset()); if (Bytes.equals(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(), LABEL_QUALIFIER, 0, LABEL_QUALIFIER.length)) { if (m instanceof Put) { existingLabels.put(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()), labelOrdinal); isNewLabels = true; } } else { String user = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); List<Integer> auths = userAuths.get(user); if (auths == null) { auths = new ArrayList<Integer>(); userAuths.put(user, auths); } if (m instanceof Delete) { auths.remove(Integer.valueOf(labelOrdinal)); } else { auths.add(labelOrdinal); } isUserAuthsChange = true; } } } } } if (isNewLabels) { byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(existingLabels); this.visibilityManager.writeToZookeeper(serialized, true); } if (isUserAuthsChange) { byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths); this.visibilityManager.writeToZookeeper(serialized, false); } } } private Pair<Map<String, Integer>, Map<String, List<Integer>>> extractLabelsAndAuths( List<List<Cell>> labelDetails) { Map<String, Integer> labels = new HashMap<String, Integer>(); Map<String, List<Integer>> userAuths = new HashMap<String, List<Integer>>(); for (List<Cell> cells : labelDetails) { for (Cell cell : cells) { if (Bytes.equals(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(), LABEL_QUALIFIER, 0, LABEL_QUALIFIER.length)) { labels.put(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()), Bytes.toInt(cell.getRowArray(), cell.getRowOffset())); } else { // These are user cells who has authorization for this label String user = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); List<Integer> auths = userAuths.get(user); if (auths == null) { auths = new ArrayList<Integer>(); userAuths.put(user, auths); } auths.add(Bytes.toInt(cell.getRowArray(), cell.getRowOffset())); } } } return new Pair<Map<String, Integer>, Map<String, List<Integer>>>(labels, userAuths); } // Checks whether cell contains any tag with type as VISIBILITY_TAG_TYPE. // This tag type is reserved and should not be explicitly set by user. private boolean checkForReservedVisibilityTagPresence(Cell cell) throws IOException { if (cell.getTagsLength() > 0) { Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength()); while (tagsItr.hasNext()) { if (tagsItr.next().getType() == VisibilityUtils.VISIBILITY_TAG_TYPE) { return false; } } } return true; } private List<Tag> createVisibilityTags(String visibilityLabelsExp) throws IOException, ParseException, InvalidLabelException { ExpressionNode node = null; node = this.expressionParser.parse(visibilityLabelsExp); node = this.expressionExpander.expand(node); List<Tag> tags = new ArrayList<Tag>(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); if (node.isSingleNode()) { writeLabelOrdinalsToStream(node, dos); tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray())); baos.reset(); } else { NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node; if (nlNode.getOperator() == Operator.OR) { for (ExpressionNode child : nlNode.getChildExps()) { writeLabelOrdinalsToStream(child, dos); tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray())); baos.reset(); } } else { writeLabelOrdinalsToStream(nlNode, dos); tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray())); baos.reset(); } } return tags; } private void writeLabelOrdinalsToStream(ExpressionNode node, DataOutputStream dos) throws IOException, InvalidLabelException { if (node.isSingleNode()) { String identifier = null; int labelOrdinal = 0; if (node instanceof LeafExpressionNode) { identifier = ((LeafExpressionNode) node).getIdentifier(); if (LOG.isTraceEnabled()) { LOG.trace("The identifier is " + identifier); } labelOrdinal = this.visibilityManager.getLabelOrdinal(identifier); } else { // This is a NOT node. LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node).getChildExps() .get(0); identifier = lNode.getIdentifier(); labelOrdinal = this.visibilityManager.getLabelOrdinal(identifier); labelOrdinal = -1 * labelOrdinal; // Store NOT node as -ve ordinal. } if (labelOrdinal == 0) { throw new InvalidLabelException("Invalid visibility label " + identifier); } StreamUtils.writeRawVInt32(dos, labelOrdinal); } else { List<ExpressionNode> childExps = ((NonLeafExpressionNode) node).getChildExps(); for (ExpressionNode child : childExps) { writeLabelOrdinalsToStream(child, dos); } } } @Override public RegionScanner preScannerOpen(ObserverContext<RegionCoprocessorEnvironment> e, Scan scan, RegionScanner s) throws IOException { HRegion region = e.getEnvironment().getRegion(); Authorizations authorizations = null; // If a super user issues a scan, he should be able to scan the cells // irrespective of the Visibility labels if (checkIfScanOrGetFromSuperUser()) { return s; } try { authorizations = scan.getAuthorizations(); } catch (DeserializationException de) { throw new IOException(de); } Filter visibilityLabelFilter = createVisibilityLabelFilter(region, authorizations); if (visibilityLabelFilter != null) { Filter filter = scan.getFilter(); if (filter != null) { scan.setFilter(new FilterList(filter, visibilityLabelFilter)); } else { scan.setFilter(visibilityLabelFilter); } } return s; } private boolean checkIfScanOrGetFromSuperUser() throws IOException { User user = getActiveUser(); if (user != null && user.getShortName() != null) { List<String> auths = this.visibilityManager.getAuths(user.getShortName()); return (auths.contains(SYSTEM_LABEL)); } return false; } @Override public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c, final Scan scan, final RegionScanner s) throws IOException { User user = getActiveUser(); if (user != null && user.getShortName() != null) { scannerOwners.put(s, user.getShortName()); } return s; } @Override public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c, final InternalScanner s, final List<Result> result, final int limit, final boolean hasNext) throws IOException { requireScannerOwner(s); return hasNext; } @Override public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c, final InternalScanner s) throws IOException { requireScannerOwner(s); } @Override public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c, final InternalScanner s) throws IOException { // clean up any associated owner mapping scannerOwners.remove(s); } /** * Verify, when servicing an RPC, that the caller is the scanner owner. If so, we assume that * access control is correctly enforced based on the checks performed in preScannerOpen() */ private void requireScannerOwner(InternalScanner s) throws AccessDeniedException { if (RequestContext.isInRequestContext()) { String requestUName = RequestContext.getRequestUserName(); String owner = scannerOwners.get(s); if (owner != null && !owner.equals(requestUName)) { throw new AccessDeniedException("User '" + requestUName + "' is not the scanner owner!"); } } } @Override public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException { Authorizations authorizations = null; // If a super user issues a get, he should be able to scan the cells // irrespective of the Visibility labels if (checkIfScanOrGetFromSuperUser()) { return; } try { authorizations = get.getAuthorizations(); } catch (DeserializationException de) { throw new IOException(de); } Filter visibilityLabelFilter = createVisibilityLabelFilter(e.getEnvironment().getRegion(), authorizations); if (visibilityLabelFilter != null) { Filter filter = get.getFilter(); if (filter != null) { get.setFilter(new FilterList(filter, visibilityLabelFilter)); } else { get.setFilter(visibilityLabelFilter); } } } private Filter createVisibilityLabelFilter(HRegion region, Authorizations authorizations) throws IOException { Map<ByteRange, Integer> cfVsMaxVersions = new HashMap<ByteRange, Integer>(); for (HColumnDescriptor hcd : region.getTableDesc().getFamilies()) { cfVsMaxVersions.put(new SimpleByteRange(hcd.getName()), hcd.getMaxVersions()); } if (authorizations == null) { // No Authorizations present for this scan/Get! // In case of system tables other than "labels" just scan with out visibility check and // filtering. Checking visibility labels for META and NAMESPACE table is not needed. TableName table = region.getRegionInfo().getTable(); if (table.isSystemTable() && !table.equals(LABELS_TABLE_NAME)) { return null; } } else { for (String label : authorizations.getLabels()) { if (!VisibilityLabelsValidator.isValidLabel(label)) { throw new IllegalArgumentException("Invalid authorization label : " + label + ". Authorizations cannot contain '(', ')' ,'&' ,'|', '!'" + " and cannot be empty"); } } } Filter visibilityLabelFilter = null; if (this.scanLabelGenerators != null) { List<String> labels = null; for (ScanLabelGenerator scanLabelGenerator : this.scanLabelGenerators) { try { // null authorizations to be handled inside SLG impl. labels = scanLabelGenerator.getLabels(getActiveUser(), authorizations); labels = (labels == null) ? new ArrayList<String>() : labels; authorizations = new Authorizations(labels); } catch (Throwable t) { LOG.error(t); throw new IOException(t); } } int labelsCount = this.visibilityManager.getLabelsCount(); BitSet bs = new BitSet(labelsCount + 1); // ordinal is index 1 based if (labels != null) { for (String label : labels) { int labelOrdinal = this.visibilityManager.getLabelOrdinal(label); if (labelOrdinal != 0) { bs.set(labelOrdinal); } } } visibilityLabelFilter = new VisibilityLabelFilter(bs, cfVsMaxVersions); } return visibilityLabelFilter; } private User getActiveUser() throws IOException { User user = RequestContext.getRequestUser(); if (!RequestContext.isInRequestContext()) { // for non-rpc handling, fallback to system user user = User.getCurrent(); } if (LOG.isTraceEnabled()) { LOG.trace("Current active user name is " + user.getShortName()); } return user; } private List<String> getSystemAndSuperUsers() throws IOException { User user = User.getCurrent(); if (user == null) { throw new IOException("Unable to obtain the current user, " + "authorization checks for internal operations will not work correctly!"); } if (LOG.isTraceEnabled()) { LOG.trace("Current user name is " + user.getShortName()); } String currentUser = user.getShortName(); List<String> superUsers = Lists.asList(currentUser, this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0])); return superUsers; } private boolean isSystemOrSuperUser() throws IOException { List<String> superUsers = getSystemAndSuperUsers(); User activeUser = getActiveUser(); return superUsers.contains(activeUser.getShortName()); } @Override public Cell postMutationBeforeWAL(ObserverContext<RegionCoprocessorEnvironment> ctx, MutationType opType, Mutation mutation, Cell oldCell, Cell newCell) throws IOException { List<Tag> tags = Lists.newArrayList(); CellVisibility cellVisibility = null; try { cellVisibility = mutation.getCellVisibility(); } catch (DeserializationException e) { throw new IOException(e); } if (cellVisibility == null) { return newCell; } // Adding all other tags Iterator<Tag> tagsItr = CellUtil.tagsIterator(newCell.getTagsArray(), newCell.getTagsOffset(), newCell.getTagsLength()); while (tagsItr.hasNext()) { Tag tag = tagsItr.next(); if (tag.getType() != VisibilityUtils.VISIBILITY_TAG_TYPE) { tags.add(tag); } } try { tags.addAll(createVisibilityTags(cellVisibility.getExpression())); } catch (ParseException e) { throw new IOException(e); } // We need to create another KV, unfortunately, because the current new KV // has no space for tags KeyValue newKv = KeyValueUtil.ensureKeyValue(newCell); KeyValue rewriteKv = new KeyValue(newKv.getRowArray(), newKv.getRowOffset(), newKv.getRowLength(), newKv.getFamilyArray(), newKv.getFamilyOffset(), newKv.getFamilyLength(), newKv.getQualifierArray(), newKv.getQualifierOffset(), newKv.getQualifierLength(), newKv.getTimestamp(), KeyValue.Type.codeToType(newKv.getTypeByte()), newKv.getValueArray(), newKv.getValueOffset(), newKv.getValueLength(), tags); // Preserve mvcc data rewriteKv.setMvccVersion(newKv.getMvccVersion()); return rewriteKv; } @Override public Service getService() { return VisibilityLabelsProtos.VisibilityLabelsService.newReflectiveService(this); } /****************************** VisibilityEndpoint service related methods ******************************/ @Override public synchronized void addLabels(RpcController controller, VisibilityLabelsRequest request, RpcCallback<VisibilityLabelsResponse> done) { VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder(); List<VisibilityLabel> labels = request.getVisLabelList(); if (!initialized) { setExceptionResults(labels.size(), new CoprocessorException("VisibilityController not yet initialized"), response); } try { checkCallingUserAuth(); List<Mutation> puts = new ArrayList<Mutation>(labels.size()); RegionActionResult successResult = RegionActionResult.newBuilder().build(); for (VisibilityLabel visLabel : labels) { byte[] label = visLabel.getLabel().toByteArray(); String labelStr = Bytes.toString(label); if (VisibilityLabelsValidator.isValidLabel(label)) { if (this.visibilityManager.getLabelOrdinal(labelStr) > 0) { RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException(ResponseConverter.buildException( new LabelAlreadyExistsException("Label '" + labelStr + "' already exists"))); response.addResult(failureResultBuilder.build()); } else { Put p = new Put(Bytes.toBytes(ordinalCounter)); p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, label, LABELS_TABLE_TAGS); if (LOG.isDebugEnabled()) { LOG.debug("Adding the label " + labelStr); } puts.add(p); ordinalCounter++; response.addResult(successResult); } } else { RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException(ResponseConverter.buildException( new InvalidLabelException("Invalid visibility label '" + labelStr + "'"))); response.addResult(failureResultBuilder.build()); } } OperationStatus[] opStatus = this.regionEnv.getRegion() .batchMutate(puts.toArray(new Mutation[puts.size()])); int i = 0; for (OperationStatus status : opStatus) { if (status.getOperationStatusCode() != SUCCESS) { while (response.getResult(i) != successResult) i++; RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException( ResponseConverter.buildException(new DoNotRetryIOException(status.getExceptionMsg()))); response.setResult(i, failureResultBuilder.build()); } i++; } } catch (IOException e) { LOG.error(e); setExceptionResults(labels.size(), e, response); } done.run(response.build()); } private void setExceptionResults(int size, IOException e, VisibilityLabelsResponse.Builder response) { RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException(ResponseConverter.buildException(e)); RegionActionResult failureResult = failureResultBuilder.build(); for (int i = 0; i < size; i++) { response.addResult(i, failureResult); } } private void performACLCheck() throws IOException { // Do ACL check only when the security is enabled. if (this.acOn && !isSystemOrSuperUser()) { User user = getActiveUser(); throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null") + " is not authorized to perform this action."); } } private List<List<Cell>> getExistingLabelsWithAuths() throws IOException { Scan scan = new Scan(); RegionScanner scanner = this.regionEnv.getRegion().getScanner(scan); List<List<Cell>> existingLabels = new ArrayList<List<Cell>>(); try { while (true) { List<Cell> cells = new ArrayList<Cell>(); scanner.next(cells); if (cells.isEmpty()) { break; } existingLabels.add(cells); } } finally { scanner.close(); } return existingLabels; } @Override public synchronized void setAuths(RpcController controller, SetAuthsRequest request, RpcCallback<VisibilityLabelsResponse> done) { VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder(); List<ByteString> auths = request.getAuthList(); if (!initialized) { setExceptionResults(auths.size(), new CoprocessorException("VisibilityController not yet initialized"), response); } byte[] user = request.getUser().toByteArray(); try { checkCallingUserAuth(); List<Mutation> puts = new ArrayList<Mutation>(auths.size()); RegionActionResult successResult = RegionActionResult.newBuilder().build(); for (ByteString authBS : auths) { byte[] auth = authBS.toByteArray(); String authStr = Bytes.toString(auth); int labelOrdinal = this.visibilityManager.getLabelOrdinal(authStr); if (labelOrdinal == 0) { // This label is not yet added. 1st this should be added to the system RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException(ResponseConverter .buildException(new InvalidLabelException("Label '" + authStr + "' doesn't exist"))); response.addResult(failureResultBuilder.build()); } else { Put p = new Put(Bytes.toBytes(labelOrdinal)); p.addImmutable(LABELS_TABLE_FAMILY, user, DUMMY_VALUE, LABELS_TABLE_TAGS); puts.add(p); response.addResult(successResult); } } OperationStatus[] opStatus = this.regionEnv.getRegion() .batchMutate(puts.toArray(new Mutation[puts.size()])); int i = 0; for (OperationStatus status : opStatus) { if (status.getOperationStatusCode() != SUCCESS) { while (response.getResult(i) != successResult) i++; RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException( ResponseConverter.buildException(new DoNotRetryIOException(status.getExceptionMsg()))); response.setResult(i, failureResultBuilder.build()); } i++; } } catch (IOException e) { LOG.error(e); setExceptionResults(auths.size(), e, response); } done.run(response.build()); } @Override public synchronized void getAuths(RpcController controller, GetAuthsRequest request, RpcCallback<GetAuthsResponse> done) { byte[] user = request.getUser().toByteArray(); GetAuthsResponse.Builder response = GetAuthsResponse.newBuilder(); response.setUser(request.getUser()); try { List<String> labels = getUserAuthsFromLabelsTable(user); for (String label : labels) { response.addAuth(HBaseZeroCopyByteString.wrap(Bytes.toBytes(label))); } } catch (IOException e) { ResponseConverter.setControllerException(controller, e); } done.run(response.build()); } private List<String> getUserAuthsFromLabelsTable(byte[] user) throws IOException { Scan s = new Scan(); s.addColumn(LABELS_TABLE_FAMILY, user); Filter filter = createVisibilityLabelFilter(this.regionEnv.getRegion(), new Authorizations(SYSTEM_LABEL)); s.setFilter(filter); List<String> auths = new ArrayList<String>(); // We do ACL check here as we create scanner directly on region. It will not make calls to // AccessController CP methods. performACLCheck(); RegionScanner scanner = this.regionEnv.getRegion().getScanner(s); List<Cell> results = new ArrayList<Cell>(1); while (true) { scanner.next(results); if (results.isEmpty()) break; Cell cell = results.get(0); int ordinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()); String label = this.visibilityManager.getLabel(ordinal); if (label != null) { auths.add(label); } results.clear(); } return auths; } @Override public synchronized void clearAuths(RpcController controller, SetAuthsRequest request, RpcCallback<VisibilityLabelsResponse> done) { VisibilityLabelsResponse.Builder response = VisibilityLabelsResponse.newBuilder(); List<ByteString> auths = request.getAuthList(); if (!initialized) { setExceptionResults(auths.size(), new CoprocessorException("VisibilityController not yet initialized"), response); } byte[] user = request.getUser().toByteArray(); try { checkCallingUserAuth(); List<String> currentAuths = this.getUserAuthsFromLabelsTable(user); List<Mutation> deletes = new ArrayList<Mutation>(auths.size()); RegionActionResult successResult = RegionActionResult.newBuilder().build(); for (ByteString authBS : auths) { byte[] auth = authBS.toByteArray(); String authStr = Bytes.toString(auth); if (currentAuths.contains(authStr)) { int labelOrdinal = this.visibilityManager.getLabelOrdinal(authStr); assert labelOrdinal > 0; Delete d = new Delete(Bytes.toBytes(labelOrdinal)); d.deleteColumns(LABELS_TABLE_FAMILY, user); deletes.add(d); response.addResult(successResult); } else { // This label is not set for the user. RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException(ResponseConverter.buildException(new InvalidLabelException( "Label '" + authStr + "' is not set for the user " + Bytes.toString(user)))); response.addResult(failureResultBuilder.build()); } } OperationStatus[] opStatus = this.regionEnv.getRegion() .batchMutate(deletes.toArray(new Mutation[deletes.size()])); int i = 0; for (OperationStatus status : opStatus) { if (status.getOperationStatusCode() != SUCCESS) { while (response.getResult(i) != successResult) i++; RegionActionResult.Builder failureResultBuilder = RegionActionResult.newBuilder(); failureResultBuilder.setException( ResponseConverter.buildException(new DoNotRetryIOException(status.getExceptionMsg()))); response.setResult(i, failureResultBuilder.build()); } i++; } } catch (IOException e) { LOG.error(e); setExceptionResults(auths.size(), e, response); } done.run(response.build()); } private void checkCallingUserAuth() throws IOException { if (!this.acOn) { User user = getActiveUser(); if (user == null) { throw new IOException("Unable to retrieve calling user"); } List<String> auths = this.visibilityManager.getAuths(user.getShortName()); if (LOG.isTraceEnabled()) { LOG.trace("The list of auths are " + auths); } if (!auths.contains(SYSTEM_LABEL)) { throw new AccessDeniedException( "User '" + user.getShortName() + "' is not authorized to perform this action."); } } } }