Package org.apache.hadoop.hbase.security.visibility

Source Code of org.apache.hadoop.hbase.security.visibility.ExpAsStringVisibilityLabelServiceImpl

/**
* 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.TagType.VISIBILITY_TAG_TYPE;
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.VisibilityUtils.SYSTEM_LABEL;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.TagType;
import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
import org.apache.hadoop.hbase.Tag;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.OperationStatus;
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.security.access.AccessControlLists;
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.Bytes;

import com.google.common.collect.Lists;

/**
* This is a VisibilityLabelService where labels in Mutation's visibility expression will be
* persisted as Strings itself rather than ordinals in 'labels' table. Also there is no need to add
* labels to the system, prior to using them in Mutations/Authorizations.
*/
@InterfaceAudience.Private
public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelService {

  private static final Log LOG = LogFactory.getLog(ExpAsStringVisibilityLabelServiceImpl.class);

  private static final byte[] DUMMY_VALUE = new byte[0];
  private static final byte STRING_SERIALIZATION_FORMAT = 2;
  private static final Tag STRING_SERIALIZATION_FORMAT_TAG = new Tag(
      TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE,
      new byte[]{STRING_SERIALIZATION_FORMAT});
  private final ExpressionParser expressionParser = new ExpressionParser();
  private final ExpressionExpander expressionExpander = new ExpressionExpander();
  private Configuration conf;
  private HRegion labelsRegion;
  private List<ScanLabelGenerator> scanLabelGenerators;

  @Override
  public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
    // Not doing specific label add. We will just add labels in Mutation visibility expression as it
    // is along with every cell.
    OperationStatus[] status = new OperationStatus[labels.size()];
    for (int i = 0; i < labels.size(); i++) {
      status[i] = new OperationStatus(OperationStatusCode.SUCCESS);
    }
    return status;
  }

  @Override
  public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
    assert labelsRegion != null;
    OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
    Put p = new Put(user);
    for (byte[] auth : authLabels) {
      p.addImmutable(LABELS_TABLE_FAMILY, auth, DUMMY_VALUE);
    }
    this.labelsRegion.put(p);
    // This is a testing impl and so not doing any caching
    for (int i = 0; i < authLabels.size(); i++) {
      finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
    }
    return finalOpStatus;
  }

  @Override
  public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
    assert labelsRegion != null;
    OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
    List<String> currentAuths = this.getAuths(user, true);
    Delete d = new Delete(user);
    int i = 0;
    for (byte[] authLabel : authLabels) {
      String authLabelStr = Bytes.toString(authLabel);
      if (currentAuths.contains(authLabelStr)) {
        d.deleteColumns(LABELS_TABLE_FAMILY, authLabel);
      } else {
        // This label is not set for the user.
        finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
            new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
                + Bytes.toString(user)));
      }
      i++;
    }
    this.labelsRegion.delete(d);
    // This is a testing impl and so not doing any caching
    for (i = 0; i < authLabels.size(); i++) {
      if (finalOpStatus[i] == null) {
        finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
      }
    }
    return finalOpStatus;
  }

  @Override
  public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
    assert (labelsRegion != null || systemCall);
    List<String> auths = new ArrayList<String>();
    Get get = new Get(user);
    List<Cell> cells = null;
    if (labelsRegion == null) {
      Table table = null;
      try {
        table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
        Result result = table.get(get);
        cells = result.listCells();
      } finally {
        if (table != null) {
          table.close();
        }
      }
    } else {
      cells = this.labelsRegion.get(get, false);
    }
    if (cells != null) {
      for (Cell cell : cells) {
        String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
            cell.getQualifierLength());
        auths.add(auth);
      }
    }
    return auths;
  }

  @Override
  public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
      boolean checkAuths) throws IOException {
    ExpressionNode node = null;
    try {
      node = this.expressionParser.parse(visExpression);
    } catch (ParseException e) {
      throw new IOException(e);
    }
    node = this.expressionExpander.expand(node);
    List<Tag> tags = new ArrayList<Tag>();
    if (withSerializationFormat) {
      tags.add(STRING_SERIALIZATION_FORMAT_TAG);
    }
    if (node instanceof NonLeafExpressionNode
        && ((NonLeafExpressionNode) node).getOperator() == Operator.OR) {
      for (ExpressionNode child : ((NonLeafExpressionNode) node).getChildExps()) {
        tags.add(createTag(child));
      }
    } else {
      tags.add(createTag(node));
    }
    return tags;
  }

  @Override
  public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
      throws IOException {
    // If a super user issues a get/scan, he should be able to scan the cells
    // irrespective of the Visibility labels
    if (isReadFromSuperUser()) {
      return new VisibilityExpEvaluator() {
        @Override
        public boolean evaluate(Cell cell) throws IOException {
          return true;
        }
      };
    }
    List<String> authLabels = null;
    for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
      try {
        // null authorizations to be handled inside SLG impl.
        authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
        authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
        authorizations = new Authorizations(authLabels);
      } catch (Throwable t) {
        LOG.error(t);
        throw new IOException(t);
      }
    }
    final List<String> authLabelsFinal = authLabels;
    return new VisibilityExpEvaluator() {
      @Override
      public boolean evaluate(Cell cell) throws IOException {
        boolean visibilityTagPresent = false;
        // Save an object allocation where we can
        if (cell.getTagsLength() > 0) {
          Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
              cell.getTagsLength());
          while (tagsItr.hasNext()) {
            boolean includeKV = true;
            Tag tag = tagsItr.next();
            if (tag.getType() == VISIBILITY_TAG_TYPE) {
              visibilityTagPresent = true;
              int offset = tag.getTagOffset();
              int endOffset = offset + tag.getTagLength();
              while (offset < endOffset) {
                short len = Bytes.toShort(tag.getBuffer(), offset);
                offset += 2;
                if (len < 0) {
                  // This is a NOT label.
                  len = (short) (-1 * len);
                  String label = Bytes.toString(tag.getBuffer(), offset, len);
                  if (authLabelsFinal.contains(label)) {
                    includeKV = false;
                    break;
                  }
                } else {
                  String label = Bytes.toString(tag.getBuffer(), offset, len);
                  if (!authLabelsFinal.contains(label)) {
                    includeKV = false;
                    break;
                  }
                }
                offset += len;
              }
              if (includeKV) {
                // We got one visibility expression getting evaluated to true. Good to include this
                // KV in the result then.
                return true;
              }
            }
          }
        }
        return !(visibilityTagPresent);
      }
    };
  }

  protected boolean isReadFromSuperUser() throws IOException {
    byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName());
    return havingSystemAuth(user);
  }

  private Tag createTag(ExpressionNode node) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(baos);
    List<String> labels = new ArrayList<String>();
    List<String> notLabels = new ArrayList<String>();
    extractLabels(node, labels, notLabels);
    Collections.sort(labels);
    Collections.sort(notLabels);
    // We will write the NOT labels 1st followed by normal labels
    // Each of the label we will write with label length (as short 1st) followed by the label bytes.
    // For a NOT node we will write the label length as -ve.
    for (String label : notLabels) {
      byte[] bLabel = Bytes.toBytes(label);
      short length = (short) bLabel.length;
      length = (short) (-1 * length);
      dos.writeShort(length);
      dos.write(bLabel);
    }
    for (String label : labels) {
      byte[] bLabel = Bytes.toBytes(label);
      dos.writeShort(bLabel.length);
      dos.write(bLabel);
    }
    return new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
  }

  private void extractLabels(ExpressionNode node, List<String> labels, List<String> notLabels) {
    if (node.isSingleNode()) {
      if (node instanceof NonLeafExpressionNode) {
        // This is a NOT node.
        LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node)
            .getChildExps().get(0);
        notLabels.add(lNode.getIdentifier());
      } else {
        labels.add(((LeafExpressionNode) node).getIdentifier());
      }
    } else {
      // A non leaf expression of labels with & operator.
      NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
      assert nlNode.getOperator() == Operator.AND;
      List<ExpressionNode> childExps = nlNode.getChildExps();
      for (ExpressionNode child : childExps) {
        extractLabels(child, labels, notLabels);
      }
    }
  }

  @Override
  public Configuration getConf() {
    return this.conf;
  }

  @Override
  public void setConf(Configuration conf) {
    this.conf = conf;
  }

  @Override
  public void init(RegionCoprocessorEnvironment e) throws IOException {
    this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
    if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
      this.labelsRegion = e.getRegion();
      // Set auth for "system" label for all super users.
      List<String> superUsers = getSystemAndSuperUsers();
      for (String superUser : superUsers) {
        byte[] user = Bytes.toBytes(superUser);
        List<String> auths = this.getAuths(user, true);
        if (auths == null || auths.isEmpty()) {
          Put p = new Put(user);
          p.addImmutable(LABELS_TABLE_FAMILY, Bytes.toBytes(SYSTEM_LABEL), DUMMY_VALUE);
          labelsRegion.put(p);
        }
      }
    }
  }

  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;
  }

  @Override
  public boolean havingSystemAuth(byte[] user) throws IOException {
    List<String> auths = this.getAuths(user, true);
    return auths.contains(SYSTEM_LABEL);
  }

  @Override
  public boolean matchVisibility(List<Tag> putTags, Byte putTagsFormat, List<Tag> deleteTags,
      Byte deleteTagsFormat) throws IOException {
    assert putTagsFormat == STRING_SERIALIZATION_FORMAT;
    assert deleteTagsFormat == STRING_SERIALIZATION_FORMAT;
    return checkForMatchingVisibilityTagsWithSortedOrder(putTags, deleteTags);
  }

  private static boolean checkForMatchingVisibilityTagsWithSortedOrder(List<Tag> putVisTags,
      List<Tag> deleteVisTags) {
    boolean matchFound = false;
    // If the size does not match. Definitely we are not comparing the equal tags.
    if ((deleteVisTags.size()) == putVisTags.size()) {
      for (Tag tag : deleteVisTags) {
        matchFound = false;
        for (Tag givenTag : putVisTags) {
          if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
              givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
            matchFound = true;
            break;
          }
        }
        if (!matchFound) break;
      }
    }
    return matchFound;
  }
}
TOP

Related Classes of org.apache.hadoop.hbase.security.visibility.ExpAsStringVisibilityLabelServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.