diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java index 4af28b2..a2648e9 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java @@ -641,6 +641,13 @@ public class AccessControlLists { return aclKey.substring(GROUP_PREFIX.length()); } + /** + * Returns the group entry with the group prefix for a group principal. + */ + public static String toGroupEntry(String name) { + return GROUP_PREFIX + name; + } + public static boolean isNamespaceEntry(String entryName) { return entryName.charAt(0) == NAMESPACE_PREFIX; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java index 2d0e446..016896e 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java @@ -81,6 +81,7 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService private VisibilityLabelsCache labelsCache; private List scanLabelGenerators; private List superUsers; + private List superGroups; static { ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -117,7 +118,7 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService throw ioe; } this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf); - this.superUsers = getSystemAndSuperUsers(); + initSystemAndSuperUsers(); if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) { this.labelsRegion = e.getRegion(); Pair, Map>> labelsAndUserAuths = @@ -203,7 +204,9 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService } } - protected List getSystemAndSuperUsers() throws IOException { + protected void initSystemAndSuperUsers() throws IOException { + this.superUsers = new ArrayList(); + this.superGroups = new ArrayList(); User user = User.getCurrent(); if (user == null) { throw new IOException("Unable to obtain the current user, " @@ -213,9 +216,17 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService LOG.trace("Current user name is " + user.getShortName()); } String currentUser = user.getShortName(); - List superUsers = Lists.asList(currentUser, + List superUserList = Lists.asList(currentUser, this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0])); - return superUsers; + if (superUserList != null) { + for (String name : superUserList) { + if (AccessControlLists.isGroupPrincipal(name)) { + this.superGroups.add(AccessControlLists.getGroupName(name)); + } else { + this.superUsers.add(name); + } + } + } } @Override @@ -276,7 +287,14 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService public OperationStatus[] clearAuths(byte[] user, List authLabels) throws IOException { assert labelsRegion != null; OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()]; - List currentAuths = this.getAuths(user, true); + List currentAuths; + if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) { + String group = AccessControlLists.getGroupName(Bytes.toString(user)); + currentAuths = this.getAuths(null, new String[]{group}, true); + } + else { + currentAuths = this.getAuths(user, null, true); + } List deletes = new ArrayList(authLabels.size()); int i = 0; for (byte[] authLabel : authLabels) { @@ -329,13 +347,21 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService } @Override - public List getAuths(byte[] user, boolean systemCall) throws IOException { + public List getAuths(byte[] user, String[] groups, boolean systemCall) + throws IOException { assert (labelsRegion != null || systemCall); if (systemCall || labelsRegion == null) { - return this.labelsCache.getAuths(Bytes.toString(user)); + return this.labelsCache.getAuths(Bytes.toString(user), groups); } Scan s = new Scan(); - s.addColumn(LABELS_TABLE_FAMILY, user); + if (user != null && user.length > 0) { + s.addColumn(LABELS_TABLE_FAMILY, user); + } + if (groups != null && groups.length > 0) { + for (String group : groups) { + s.addFamily(Bytes.toBytes(AccessControlLists.toGroupEntry(group))); + } + } Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion, new Authorizations(SYSTEM_LABEL)); s.setFilter(filter); @@ -385,7 +411,8 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService boolean checkAuths) throws IOException { Set auths = null; if (checkAuths) { - auths = this.labelsCache.getAuthsAsOrdinals(VisibilityUtils.getActiveUser().getShortName()); + User user = VisibilityUtils.getActiveUser(); + auths = this.labelsCache.getAuthsAsOrdinals(user.getShortName(), user.getGroupNames()); } return VisibilityUtils.createVisibilityExpTags(visExpression, withSerializationFormat, checkAuths, auths, labelsCache); @@ -494,26 +521,37 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService } protected boolean isReadFromSystemAuthUser() throws IOException { - byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName()); + User user = VisibilityUtils.getActiveUser(); return havingSystemAuth(user); } @Override - public boolean havingSystemAuth(byte[] user) throws IOException { + public boolean havingSystemAuth(User user) throws IOException { // A super user has 'system' auth. if (isSystemOrSuperUser(user)) { return true; } // A user can also be explicitly granted 'system' auth. - List auths = this.getAuths(user, true); + List auths = this.getAuths(Bytes.toBytes(user.getShortName()), user.getGroupNames(), true); if (LOG.isTraceEnabled()) { - LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths); + LOG.trace("The auths for user " + user.getShortName() + " are " + auths); } return auths.contains(SYSTEM_LABEL); } - protected boolean isSystemOrSuperUser(byte[] user) throws IOException { - return this.superUsers.contains(Bytes.toString(user)); + protected boolean isSystemOrSuperUser(User user) throws IOException { + if (this.superUsers.contains(user.getShortName())) { + return true; + } + String[] groups = user.getGroupNames(); + if (groups != null) { + for (String group : groups) { + if (this.superGroups.contains(group)) { + return true; + } + } + } + return false; } @Override diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefinedSetFilterScanLabelGenerator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefinedSetFilterScanLabelGenerator.java index a42def0..bb2fe00 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefinedSetFilterScanLabelGenerator.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefinedSetFilterScanLabelGenerator.java @@ -60,7 +60,7 @@ public class DefinedSetFilterScanLabelGenerator implements ScanLabelGenerator { if (authorizations != null) { List labels = authorizations.getLabels(); String userName = user.getShortName(); - List auths = this.labelsCache.getAuths(userName); + List auths = this.labelsCache.getAuths(userName, user.getGroupNames()); return dropLabelsNotInUserAuths(labels, auths, userName); } return null; diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/EnforcingScanLabelGenerator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/EnforcingScanLabelGenerator.java index 00a65d9..bbaac90 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/EnforcingScanLabelGenerator.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/EnforcingScanLabelGenerator.java @@ -59,7 +59,7 @@ public class EnforcingScanLabelGenerator implements ScanLabelGenerator { if (authorizations != null) { LOG.warn("Dropping authorizations requested by user " + userName + ": " + authorizations); } - return this.labelsCache.getAuths(userName); + return this.labelsCache.getAuths(userName, user.getGroupNames()); } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/FeedUserAuthScanLabelGenerator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/FeedUserAuthScanLabelGenerator.java index 3decbe8..05911a6 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/FeedUserAuthScanLabelGenerator.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/FeedUserAuthScanLabelGenerator.java @@ -62,7 +62,7 @@ public class FeedUserAuthScanLabelGenerator implements ScanLabelGenerator { if (authorizations == null || authorizations.getLabels() == null || authorizations.getLabels().isEmpty()) { String userName = user.getShortName(); - return this.labelsCache.getAuths(userName); + return this.labelsCache.getAuths(userName, user.getGroupNames()); } return authorizations.getLabels(); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java index 9deeca3..3b69390 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java @@ -825,7 +825,13 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements + (requestingUser != null ? requestingUser.getShortName() : "null") + "' is not authorized to perform this action."); } - labels = this.visibilityLabelService.getAuths(user, false); + if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) { + String group = AccessControlLists.getGroupName(Bytes.toString(user)); + labels = this.visibilityLabelService.getAuths(null, new String[]{group}, false); + } + else { + labels = this.visibilityLabelService.getAuths(user, null, false); + } } catch (IOException e) { ResponseConverter.setControllerException(controller, e); } @@ -919,7 +925,7 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements if (user == null) { throw new IOException("Unable to retrieve calling user"); } - if (!(this.visibilityLabelService.havingSystemAuth(Bytes.toBytes(user.getShortName())))) { + if (!(this.visibilityLabelService.havingSystemAuth(user))) { throw new AccessDeniedException("User '" + user.getShortName() + "' is not authorized to perform this action."); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java index 1f74a9a..bb69c84 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java @@ -27,6 +27,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; import org.apache.hadoop.hbase.regionserver.OperationStatus; +import org.apache.hadoop.hbase.security.User; /** * The interface which deals with visibility labels and user auths admin service as well as the cell @@ -73,13 +74,18 @@ public interface VisibilityLabelService extends Configurable { OperationStatus[] clearAuths(byte[] user, List authLabels) throws IOException; /** + * Retrieve the visibility labels for the user and groups. * @param user * Name of the user whose authorization to be retrieved + * Can be null if only group authorizations are to be retrieved + * @param groups + * List of groups whose authorization to be retrieved + * Can be null if only user authorizations are to be retrieved * @param systemCall * Whether a system or user originated call. * @return Visibility labels authorized for the given user. */ - List getAuths(byte[] user, boolean systemCall) throws IOException; + List getAuths(byte[] user, String[] groups, boolean systemCall) throws IOException; /** * Retrieve the list of visibility labels defined in the system. @@ -124,7 +130,7 @@ public interface VisibilityLabelService extends Configurable { * User for whom system auth check to be done. * @return true if the given user is having system/super auth */ - boolean havingSystemAuth(byte[] user) throws IOException; + boolean havingSystemAuth(User user) throws IOException; /** * System uses this for deciding whether a Cell can be deleted by matching visibility expression diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java index a5c2155..c705bd9 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java @@ -35,6 +35,8 @@ import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.MultiUserAuthorizations; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.UserAuthorizations; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.AccessControlLists; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import org.apache.zookeeper.KeeperException; @@ -57,6 +59,8 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider { private Map labels = new HashMap(); private Map ordinalVsLabels = new HashMap(); private Map> userAuths = new HashMap>(); + private Map> groupAuths = new HashMap>(); + /** * This covers the members labels, ordinalVsLabels and userAuths */ @@ -141,7 +145,12 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider { this.userAuths.clear(); for (UserAuthorizations userAuths : multiUserAuths.getUserAuthsList()) { String user = Bytes.toString(userAuths.getUser().toByteArray()); - this.userAuths.put(user, new HashSet(userAuths.getAuthList())); + if (AccessControlLists.isGroupPrincipal(user)) { + this.groupAuths.put(AccessControlLists.getGroupName(user), + new HashSet(userAuths.getAuthList())); + } else { + this.userAuths.put(user, new HashSet(userAuths.getAuthList())); + } } } finally { this.lock.writeLock().unlock(); @@ -196,19 +205,14 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider { } } - public List getAuths(String user) { + public List getAuths(String user, String[] groups) { List auths = EMPTY_LIST; - this.lock.readLock().lock(); - try { - Set authOrdinals = userAuths.get(user); - if (authOrdinals != null) { - auths = new ArrayList(authOrdinals.size()); - for (Integer authOrdinal : authOrdinals) { - auths.add(ordinalVsLabels.get(authOrdinal)); - } + Set authOrdinals = getAuthsAsOrdinals(user, groups); + if (!authOrdinals.equals(EMPTY_SET)) { + auths = new ArrayList(authOrdinals.size()); + for (Integer authOrdinal : authOrdinals) { + auths.add(ordinalVsLabels.get(authOrdinal)); } - } finally { - this.lock.readLock().unlock(); } return auths; } @@ -216,14 +220,32 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider { /** * Returns the list of ordinals of authentications associated with the user * - * @param user Not null value. + * @param user + * @param groups * @return the list of ordinals */ - public Set getAuthsAsOrdinals(String user) { + public Set getAuthsAsOrdinals(String user, String[] groups) { this.lock.readLock().lock(); try { - Set auths = userAuths.get(user); - return (auths == null) ? EMPTY_SET : auths; + Set authOrdinals = new HashSet(); + if (user != null) { + Set userAuthOrdinals = userAuths.get(user); + if (userAuthOrdinals != null && !userAuthOrdinals.isEmpty()) { + authOrdinals.addAll(userAuthOrdinals); + } + } + if (groups != null && groups.length > 0) { + Set groupAuthOrdinals = null; + if (groups != null) { + for (String group : groups) { + groupAuthOrdinals = groupAuths.get(group); + if (groupAuthOrdinals != null && !groupAuthOrdinals.isEmpty()) { + authOrdinals.addAll(groupAuthOrdinals); + } + } + } + } + return (authOrdinals.isEmpty()) ? EMPTY_SET : authOrdinals; } finally { this.lock.readLock().unlock(); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java index cc615ed..46c5575 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java @@ -80,6 +80,7 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer private HRegion labelsRegion; private List scanLabelGenerators; private List superUsers; + private List superGroups; @Override public OperationStatus[] addLabels(List labels) throws IOException { @@ -113,7 +114,14 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer public OperationStatus[] clearAuths(byte[] user, List authLabels) throws IOException { assert labelsRegion != null; OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()]; - List currentAuths = this.getAuths(user, true); + List currentAuths; + if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) { + String group = AccessControlLists.getGroupName(Bytes.toString(user)); + currentAuths = this.getAuths(null, new String[]{group}, true); + } + else { + currentAuths = this.getAuths(user, null, true); + } Delete d = new Delete(user); int i = 0; for (byte[] authLabel : authLabels) { @@ -139,30 +147,59 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer } @Override - public List getAuths(byte[] user, boolean systemCall) throws IOException { + public List getAuths(byte[] user, String[] groups, boolean systemCall) throws IOException { assert (labelsRegion != null || systemCall); List auths = new ArrayList(); - Get get = new Get(user); - List 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(); + if (user != null && user.length > 0) { + Get get = new Get(user); + List 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); } - } else { - cells = this.labelsRegion.get(get, false); - } - if (cells != null) { - for (Cell cell : cells) { - String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), + if (cells != null) { + for (Cell cell : cells) { + String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); - auths.add(auth); + auths.add(auth); + } + } + } + if (groups != null && groups.length > 0) { + for (String group : groups) { + Get get = new Get(Bytes.toBytes(AccessControlLists.toGroupEntry(group))); + List 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; @@ -275,7 +312,7 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer } protected boolean isReadFromSystemAuthUser() throws IOException { - byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName()); + User user = VisibilityUtils.getActiveUser(); return havingSystemAuth(user); } @@ -340,13 +377,15 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer @Override public void init(RegionCoprocessorEnvironment e) throws IOException { this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf); - this.superUsers = getSystemAndSuperUsers(); + initSystemAndSuperUsers(); if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) { this.labelsRegion = e.getRegion(); } } - private List getSystemAndSuperUsers() throws IOException { + private void initSystemAndSuperUsers() throws IOException { + this.superUsers = new ArrayList(); + this.superGroups = new ArrayList(); User user = User.getCurrent(); if (user == null) { throw new IOException("Unable to obtain the current user, " @@ -356,21 +395,40 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer LOG.trace("Current user name is " + user.getShortName()); } String currentUser = user.getShortName(); - List superUsers = Lists.asList(currentUser, + List superUserList = Lists.asList(currentUser, this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0])); - return superUsers; + if (superUserList != null) { + for (String name : superUserList) { + if (AccessControlLists.isGroupPrincipal(name)) { + this.superGroups.add(AccessControlLists.getGroupName(name)); + } else { + this.superUsers.add(name); + } + } + }; } - protected boolean isSystemOrSuperUser(byte[] user) throws IOException { - return this.superUsers.contains(Bytes.toString(user)); + protected boolean isSystemOrSuperUser(User user) throws IOException { + if (this.superUsers.contains(user.getShortName())) { + return true; + } + String[] groups = user.getGroupNames(); + if (groups != null) { + for (String group : groups) { + if (this.superGroups.contains(group)) { + return true; + } + } + } + return false; } @Override - public boolean havingSystemAuth(byte[] user) throws IOException { + public boolean havingSystemAuth(User user) throws IOException { if (isSystemOrSuperUser(user)) { return true; } - List auths = this.getAuths(user, true); + List auths = this.getAuths(Bytes.toBytes(user.getShortName()), user.getGroupNames(), true); return auths.contains(SYSTEM_LABEL); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLablesWihGroups.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLablesWihGroups.java new file mode 100644 index 0000000..b69c30e --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLablesWihGroups.java @@ -0,0 +1,255 @@ +/** + * 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.security.visibility.VisibilityConstants.LABELS_TABLE_NAME; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.security.PrivilegedExceptionAction; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellScanner; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.ConnectionFactory; +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.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; + +@Category({SecurityTests.class, MediumTests.class}) +public class TestVisibilityLablesWihGroups { + + public static final String CONFIDENTIAL = "confidential"; + private static final String SECRET = "secret"; + public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private static final byte[] ROW_1 = Bytes.toBytes("row1"); + private final static byte[] CF = Bytes.toBytes("f"); + private final static byte[] Q1 = Bytes.toBytes("q1"); + private final static byte[] Q2 = Bytes.toBytes("q2"); + private final static byte[] Q3 = Bytes.toBytes("q3"); + private final static byte[] value1 = Bytes.toBytes("value1"); + private final static byte[] value2 = Bytes.toBytes("value2"); + private final static byte[] value3 = Bytes.toBytes("value3"); + public static Configuration conf; + + @Rule + public final TestName TEST_NAME = new TestName(); + public static User SUPERUSER; + public static User TESTUSER; + + @BeforeClass + public static void setupBeforeClass() throws Exception { + // setup configuration + conf = TEST_UTIL.getConfiguration(); + VisibilityTestUtil.enableVisiblityLabels(conf); + // Not setting any SLG class. This means to use the default behavior. + // Use a group as the super user. + conf.set("hbase.superuser", "@supergroup"); + TEST_UTIL.startMiniCluster(1); + // 'admin' still has super user permission because it is part of the 'supergroup' + SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); + // 'test' user will inherit 'testgroup' vi + TESTUSER = User.createUserForTesting(conf, "test", new String[] {"testgroup" }); + + // Wait for the labels table to become available + TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); + + // Set up for the test + SUPERUSER.runAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + try { + VisibilityClient.addLabels(conf, new String[] { SECRET, CONFIDENTIAL }); + // set auth for @testgroup + VisibilityClient.setAuths(conf, new String[] { CONFIDENTIAL }, "@testgroup"); + } catch (Throwable t) { + throw new IOException(t); + } + return null; + } + }); + } + + @Test + public void testDefaultScanLabelGeneratorStack() throws Exception { + final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); + + SUPERUSER.runAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Table table = TEST_UTIL.createTable(tableName, CF); + try { + Put put = new Put(ROW_1); + put.add(CF, Q1, HConstants.LATEST_TIMESTAMP, value1); + put.setCellVisibility(new CellVisibility(SECRET)); + table.put(put); + put = new Put(ROW_1); + put.add(CF, Q2, HConstants.LATEST_TIMESTAMP, value2); + put.setCellVisibility(new CellVisibility(CONFIDENTIAL)); + table.put(put); + put = new Put(ROW_1); + put.add(CF, Q3, HConstants.LATEST_TIMESTAMP, value3); + table.put(put); + return null; + } finally { + table.close(); + } + } + }); + + // Test that super user can see all the cells. + SUPERUSER.runAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName); + try { + Scan s = new Scan(); + ResultScanner scanner = table.getScanner(s); + Result[] next = scanner.next(1); + + // Test that super user can see all the cells. + assertTrue(next.length == 1); + CellScanner cellScanner = next[0].cellScanner(); + cellScanner.advance(); + Cell current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current.getQualifier(), Q1)); + assertTrue(Bytes.equals(current.getValue(), value1)); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current.getQualifier(), Q2)); + assertTrue(Bytes.equals(current.getValue(), value2)); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current.getQualifier(), Q3)); + assertTrue(Bytes.equals(current.getValue(), value3)); + + return null; + } finally { + table.close(); + connection.close(); + } + } + }); + + TESTUSER.runAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Table table = new HTable(conf, tableName); + try { + // Test scan with no auth attribute + Scan s = new Scan(); + ResultScanner scanner = table.getScanner(s); + Result[] next = scanner.next(1); + + assertTrue(next.length == 1); + CellScanner cellScanner = next[0].cellScanner(); + cellScanner.advance(); + Cell current = cellScanner.current(); + // test user can see value2 (CONFIDENTIAL) and value3 (no label) + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current.getQualifier(), Q2)); + assertTrue(Bytes.equals(current.getValue(), value2)); + cellScanner.advance(); + current = cellScanner.current(); + // test user can see value2 (CONFIDENTIAL) and value3 (no label) + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current.getQualifier(), Q3)); + assertTrue(Bytes.equals(current.getValue(), value3)); + + // Test scan with correct auth attribute for test user + Scan s1 = new Scan(); + // test user is entitled to 'CONFIDENTIAL'. + // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs. + s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL })); + ResultScanner scanner1 = table.getScanner(s1); + Result[] next1 = scanner1.next(1); + + assertTrue(next1.length == 1); + CellScanner cellScanner1 = next1[0].cellScanner(); + cellScanner1.advance(); + Cell current1 = cellScanner1.current(); + // test user can see value2 (CONFIDENTIAL) and value3 (no label) + assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), + current1.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current1.getQualifier(), Q2)); + assertTrue(Bytes.equals(current1.getValue(), value2)); + cellScanner1.advance(); + current1 = cellScanner1.current(); + // test user can see value2 (CONFIDENTIAL) and value3 (no label) + assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), + current1.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current1.getQualifier(), Q3)); + assertTrue(Bytes.equals(current1.getValue(), value3)); + + // Test scan with incorrect auth attribute for test user + Scan s2 = new Scan(); + // test user is entitled to 'CONFIDENTIAL'. + // If we set 'SECRET', it will be dropped by the SLGs. + s2.setAuthorizations(new Authorizations(new String[] { SECRET })); + ResultScanner scanner2 = table.getScanner(s2); + Result next2 = scanner2.next(); + CellScanner cellScanner2 = next2.cellScanner(); + cellScanner2.advance(); + Cell current2 = cellScanner2.current(); + // This scan will only see value3 (no label) + assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(), + current2.getRowLength(), ROW_1, 0, ROW_1.length)); + assertTrue(Bytes.equals(current2.getQualifier(), Q3)); + assertTrue(Bytes.equals(current2.getValue(), value3)); + + assertFalse(cellScanner2.advance()); + + return null; + } finally { + table.close(); + } + } + }); + + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } +}