Return-Path: X-Original-To: apmail-hbase-commits-archive@www.apache.org Delivered-To: apmail-hbase-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 6C1D7102DF for ; Tue, 13 Aug 2013 21:50:33 +0000 (UTC) Received: (qmail 1314 invoked by uid 500); 13 Aug 2013 21:50:33 -0000 Delivered-To: apmail-hbase-commits-archive@hbase.apache.org Received: (qmail 1266 invoked by uid 500); 13 Aug 2013 21:50:33 -0000 Mailing-List: contact commits-help@hbase.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@hbase.apache.org Delivered-To: mailing list commits@hbase.apache.org Received: (qmail 1252 invoked by uid 99); 13 Aug 2013 21:50:33 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 13 Aug 2013 21:50:33 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 13 Aug 2013 21:50:22 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 439FB23889ED; Tue, 13 Aug 2013 21:49:59 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1513666 [3/4] - in /hbase/trunk: hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ hbase-client/src/main/java/org/apache/hadoop/hbase/security/access/ hbase-common/src/main/java/org/apache/hadoop/hbase/ hbase-protocol/src/main/j... Date: Tue, 13 Aug 2013 21:49:57 -0000 To: commits@hbase.apache.org From: stack@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20130813214959.439FB23889ED@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: hbase/trunk/hbase-protocol/src/main/protobuf/AccessControl.proto URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-protocol/src/main/protobuf/AccessControl.proto?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-protocol/src/main/protobuf/AccessControl.proto (original) +++ hbase/trunk/hbase-protocol/src/main/protobuf/AccessControl.proto Tue Aug 13 21:49:56 2013 @@ -32,55 +32,76 @@ message Permission { CREATE = 3; ADMIN = 4; } - repeated Action action = 1; - optional TableName tableName = 2; - optional bytes family = 3; - optional bytes qualifier = 4; + enum Type { + Global = 1; + Namespace = 2; + Table = 3; + } + required Type type = 1; + optional GlobalPermission global_permission = 2; + optional NamespacePermission namespace_permission = 3; + optional TablePermission table_permission = 4; +} + +message TablePermission { + optional TableName table_name = 1; + optional bytes family = 2; + optional bytes qualifier = 3; + repeated Permission.Action action = 4; +} + +message NamespacePermission { + optional bytes namespace_name = 1; + repeated Permission.Action action = 2; +} + +message GlobalPermission { + repeated Permission.Action action = 1; } message UserPermission { required bytes user = 1; - required Permission permission = 2; + required Permission permission = 3; } /** - * Content of the /hbase/acl/ znode. + * Content of the /hbase/acl/
znode. */ -message UserTablePermissions { +message UsersAndPermissions { message UserPermissions { required bytes user = 1; repeated Permission permissions = 2; } - repeated UserPermissions permissions = 1; + repeated UserPermissions user_permissions = 1; } message GrantRequest { - required UserPermission permission = 1; + required UserPermission user_permission = 1; } message GrantResponse { } message RevokeRequest { - required UserPermission permission = 1; - + required UserPermission user_permission = 1; } message RevokeResponse { } - message UserPermissionsRequest { - optional TableName tableName = 1; + optional Permission.Type type = 1; + optional TableName table_name = 2; + optional bytes namespace_name = 3; } message UserPermissionsResponse { - repeated UserPermission permission = 1; + repeated UserPermission user_permission = 1; } message CheckPermissionsRequest { - repeated Permission permission = 1; + repeated Permission permission = 1; } message CheckPermissionsResponse { Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/migration/NamespaceUpgrade.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/migration/NamespaceUpgrade.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/migration/NamespaceUpgrade.java (original) +++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/migration/NamespaceUpgrade.java Tue Aug 13 21:49:56 2013 @@ -20,23 +20,38 @@ package org.apache.hadoop.hbase.migration; import com.google.common.collect.Lists; +import com.google.common.primitives.Ints; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.PathFilter; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.catalog.MetaEditor; import org.apache.hadoop.hbase.exceptions.DeserializationException; +import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.regionserver.HRegion; +import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; +import org.apache.hadoop.hbase.regionserver.wal.HLog; +import org.apache.hadoop.hbase.regionserver.wal.HLogFactory; +import org.apache.hadoop.hbase.regionserver.wal.HLogUtil; +import org.apache.hadoop.hbase.security.access.AccessControlLists; import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.FSTableDescriptors; import org.apache.hadoop.hbase.util.FSUtils; import org.apache.hadoop.util.Tool; import java.io.IOException; +import java.util.Comparator; import java.util.List; /** @@ -58,6 +73,7 @@ public class NamespaceUpgrade implements private Path sysNsDir; private Path defNsDir; private Path baseDirs[]; + private Path backupDir; public NamespaceUpgrade() throws IOException { } @@ -70,6 +86,7 @@ public class NamespaceUpgrade implements baseDirs = new Path[]{rootDir, new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY), new Path(rootDir, HConstants.HBASE_TEMP_DIRECTORY)}; + backupDir = new Path(rootDir, HConstants.MIGRATION_NAME); } @@ -84,11 +101,14 @@ public class NamespaceUpgrade implements makeNamespaceDirs(); + migrateMeta(); + + migrateACL(); + migrateTables(); migrateSnapshots(); - migrateMeta(); FSUtils.setVersion(fs, rootDir); } @@ -185,6 +205,156 @@ public class NamespaceUpgrade implements + oldMetaRegionDir + " to " + newMetaRegionDir); } } + + Path oldRootDir = new Path(rootDir, "-ROOT-"); + if(!fs.rename(oldRootDir, backupDir)) { + throw new IllegalStateException("Failed to old data: "+oldRootDir+" to "+backupDir); + } + } + + public void migrateACL() throws IOException { + + TableName oldTableName = TableName.valueOf("_acl_"); + Path oldTablePath = new Path(rootDir, oldTableName.getNameAsString()); + + if(!fs.exists(oldTablePath)) { + return; + } + + LOG.info("Migrating ACL table"); + + TableName newTableName = AccessControlLists.ACL_TABLE_NAME; + Path newTablePath = FSUtils.getTableDir(rootDir, newTableName); + HTableDescriptor oldDesc = + readTableDescriptor(fs, getCurrentTableInfoStatus(fs, oldTablePath)); + + if(FSTableDescriptors.getTableInfoPath(fs, newTablePath) == null) { + LOG.info("Creating new tableDesc for ACL"); + HTableDescriptor newDesc = new HTableDescriptor(oldDesc); + newDesc.setName(newTableName); + new FSTableDescriptors(this.conf).createTableDescriptorForTableDirectory( + newTablePath, newDesc, true); + } + + + ServerName fakeServer = new ServerName("nsupgrade",96,123); + String metaLogName = HLogUtil.getHLogDirectoryName(fakeServer.toString()); + HLog metaHLog = HLogFactory.createMetaHLog(fs, rootDir, + metaLogName, conf, null, + fakeServer.toString()); + HRegion meta = HRegion.openHRegion(rootDir, HRegionInfo.FIRST_META_REGIONINFO, + HTableDescriptor.META_TABLEDESC, metaHLog, conf); + HRegion region = null; + try { + for(Path regionDir : FSUtils.getRegionDirs(fs, oldTablePath)) { + LOG.info("Migrating ACL region "+regionDir.getName()); + HRegionInfo oldRegionInfo = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir); + HRegionInfo newRegionInfo = + new HRegionInfo(newTableName, + oldRegionInfo.getStartKey(), + oldRegionInfo.getEndKey(), + oldRegionInfo.isSplit(), + oldRegionInfo.getRegionId()); + newRegionInfo.setOffline(oldRegionInfo.isOffline()); + region = + new HRegion( + HRegionFileSystem.openRegionFromFileSystem(conf, fs, oldTablePath, + oldRegionInfo, false), + metaHLog, + conf, + oldDesc, + null); + region.initialize(); + //Run major compaction to archive old stores + //to keep any snapshots to _acl_ unbroken + region.compactStores(true); + region.waitForFlushesAndCompactions(); + region.close(); + + //Create new region dir + Path newRegionDir = new Path(newTablePath, newRegionInfo.getEncodedName()); + if(!fs.exists(newRegionDir)) { + if(!fs.mkdirs(newRegionDir)) { + throw new IllegalStateException("Failed to create new region dir: " + newRegionDir); + } + } + + //create new region info file, delete in case one exists + HRegionFileSystem.openRegionFromFileSystem(conf, fs, newTablePath, newRegionInfo, false); + + //migrate region contents + for(FileStatus file : fs.listStatus(regionDir, new FSUtils.UserTableDirFilter(fs))) { + if(file.getPath().getName().equals(HRegionFileSystem.REGION_INFO_FILE)) + continue; + if(!fs.rename(file.getPath(), newRegionDir)) { + throw new IllegalStateException("Failed to move file "+file.getPath()+" to " + + newRegionDir); + } + } + meta.put(MetaEditor.makePutFromRegionInfo(newRegionInfo)); + meta.delete(MetaEditor.makeDeleteFromRegionInfo(oldRegionInfo)); + } + } finally { + meta.flushcache(); + meta.waitForFlushesAndCompactions(); + meta.close(); + metaHLog.closeAndDelete(); + if(region != null) { + region.close(); + } + } + if(!fs.rename(oldTablePath, backupDir)) { + throw new IllegalStateException("Failed to old data: "+oldTablePath+" to "+backupDir); + } + } + + //Culled from FSTableDescriptors + private static HTableDescriptor readTableDescriptor(FileSystem fs, + FileStatus status) throws IOException { + int len = Ints.checkedCast(status.getLen()); + byte [] content = new byte[len]; + FSDataInputStream fsDataInputStream = fs.open(status.getPath()); + try { + fsDataInputStream.readFully(content); + } finally { + fsDataInputStream.close(); + } + HTableDescriptor htd = null; + try { + htd = HTableDescriptor.parseFrom(content); + } catch (DeserializationException e) { + throw new IOException("content=" + Bytes.toShort(content), e); + } + return htd; + } + + private static final PathFilter TABLEINFO_PATHFILTER = new PathFilter() { + @Override + public boolean accept(Path p) { + // Accept any file that starts with TABLEINFO_NAME + return p.getName().startsWith(".tableinfo"); + } + }; + + static final Comparator TABLEINFO_FILESTATUS_COMPARATOR = + new Comparator() { + @Override + public int compare(FileStatus left, FileStatus right) { + return -left.compareTo(right); + }}; + + // logic culled from FSTableDescriptors + static FileStatus getCurrentTableInfoStatus(FileSystem fs, Path dir) + throws IOException { + FileStatus [] status = FSUtils.listStatus(fs, dir, TABLEINFO_PATHFILTER); + if (status == null || status.length < 1) return null; + FileStatus mostCurrent = null; + for (FileStatus file : status) { + if (mostCurrent == null || TABLEINFO_FILESTATUS_COMPARATOR.compare(file, mostCurrent) < 0) { + mostCurrent = file; + } + } + return mostCurrent; } public static boolean verifyNSUpgrade(FileSystem fs, Path rootDir) Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java (original) +++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java Tue Aug 13 21:49:56 2013 @@ -23,6 +23,7 @@ import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -38,6 +39,7 @@ import org.apache.hadoop.hbase.HColumnDe import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.catalog.MetaReader; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; @@ -89,17 +91,17 @@ import com.google.protobuf.InvalidProtoc */ public class AccessControlLists { /** Internal storage table for access control lists */ - public static final String ACL_TABLE_NAME_STR = "_acl_"; - public static final byte[] ACL_TABLE_NAME = Bytes.toBytes(ACL_TABLE_NAME_STR); - public static final TableName ACL_TABLE = - TableName.valueOf(ACL_TABLE_NAME); - public static final byte[] ACL_GLOBAL_NAME = ACL_TABLE_NAME; + public static final TableName ACL_TABLE_NAME = + TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl"); + public static final byte[] ACL_GLOBAL_NAME = ACL_TABLE_NAME.getName(); /** Column family used to store ACL grants */ public static final String ACL_LIST_FAMILY_STR = "l"; public static final byte[] ACL_LIST_FAMILY = Bytes.toBytes(ACL_LIST_FAMILY_STR); + public static final char NAMESPACE_PREFIX = '@'; + /** Table descriptor for ACL internal table */ - public static final HTableDescriptor ACL_TABLEDESC = new HTableDescriptor(ACL_TABLE); + public static final HTableDescriptor ACL_TABLEDESC = new HTableDescriptor(ACL_TABLE_NAME); static { ACL_TABLEDESC.addFamily( new HColumnDescriptor(ACL_LIST_FAMILY, @@ -125,7 +127,7 @@ public class AccessControlLists { * @param master reference to HMaster */ static void init(MasterServices master) throws IOException { - if (!MetaReader.tableExists(master.getCatalogTracker(), ACL_TABLE)) { + if (!MetaReader.tableExists(master.getCatalogTracker(), ACL_TABLE_NAME)) { master.createTable(ACL_TABLEDESC, null); } } @@ -139,8 +141,8 @@ public class AccessControlLists { static void addUserPermission(Configuration conf, UserPermission userPerm) throws IOException { Permission.Action[] actions = userPerm.getActions(); - - Put p = new Put(userPerm.isGlobal() ? ACL_GLOBAL_NAME : userPerm.getTable().getName()); + byte[] rowKey = userPermissionRowKey(userPerm); + Put p = new Put(rowKey); byte[] key = userPermissionKey(userPerm); if ((actions == null) || (actions.length == 0)) { @@ -155,8 +157,8 @@ public class AccessControlLists { } p.add(ACL_LIST_FAMILY, key, value); if (LOG.isDebugEnabled()) { - LOG.debug("Writing permission for table "+ - userPerm.getTable()+" "+ + LOG.debug("Writing permission with rowKey "+ + Bytes.toString(rowKey)+" "+ Bytes.toString(key)+": "+Bytes.toStringBinary(value) ); } @@ -184,8 +186,7 @@ public class AccessControlLists { */ static void removeUserPermission(Configuration conf, UserPermission userPerm) throws IOException { - - Delete d = new Delete(userPerm.isGlobal() ? ACL_GLOBAL_NAME : userPerm.getTable().getName()); + Delete d = new Delete(userPermissionRowKey(userPerm)); byte[] key = userPermissionKey(userPerm); if (LOG.isDebugEnabled()) { @@ -222,7 +223,27 @@ public class AccessControlLists { } /** - * Remove specified table column from the _acl_ table. + * Remove specified namespace from the acl table. + */ + static void removeNamespacePermissions(Configuration conf, String namespace) + throws IOException{ + Delete d = new Delete(Bytes.toBytes(toNamespaceEntry(namespace))); + + if (LOG.isDebugEnabled()) { + LOG.debug("Removing permissions of removed namespace "+ namespace); + } + + HTable acls = null; + try { + acls = new HTable(conf, ACL_TABLE_NAME); + acls.delete(d); + } finally { + if (acls != null) acls.close(); + } + } + + /** + * Remove specified table column from the acl table. */ static void removeTablePermissions(Configuration conf, TableName tableName, byte[] column) throws IOException{ @@ -269,6 +290,18 @@ public class AccessControlLists { } } + static byte[] userPermissionRowKey(UserPermission userPerm) { + byte[] row; + if(userPerm.hasNamespace()) { + row = Bytes.toBytes(toNamespaceEntry(userPerm.getNamespace())); + } else if(userPerm.isGlobal()) { + row = ACL_GLOBAL_NAME; + } else { + row = userPerm.getTable().getName(); + } + return row; + } + /** * Build qualifier key from user permission: * username @@ -295,14 +328,14 @@ public class AccessControlLists { * metadata table. */ static boolean isAclRegion(HRegion region) { - return ACL_TABLE.equals(region.getTableDesc().getTableName()); + return ACL_TABLE_NAME.equals(region.getTableDesc().getTableName()); } /** * Returns {@code true} if the given table is {@code _acl_} metadata table. */ static boolean isAclTable(HTableDescriptor desc) { - return ACL_TABLE.equals(desc.getTableName()); + return ACL_TABLE_NAME.equals(desc.getTableName()); } /** @@ -313,16 +346,16 @@ public class AccessControlLists { * @return a map of the permissions for this table. * @throws IOException */ - static Map> loadAll( + static Map> loadAll( HRegion aclRegion) throws IOException { if (!isAclRegion(aclRegion)) { - throw new IOException("Can only load permissions from "+ACL_TABLE_NAME_STR); + throw new IOException("Can only load permissions from "+ACL_TABLE_NAME); } - Map> allPerms = - new TreeMap>(); + Map> allPerms = + new TreeMap>(); // do a full scan of _acl_ table @@ -338,21 +371,21 @@ public class AccessControlLists { boolean hasNext = iScanner.next(row); ListMultimap perms = ArrayListMultimap.create(); - TableName table = null; + byte[] entry = null; for (KeyValue kv : row) { - if (table == null) { - table = TableName.valueOf(kv.getRow()); + if (entry == null) { + entry = kv.getRow(); } Pair permissionsOfUserOnTable = - parseTablePermissionRecord(table, kv); + parsePermissionRecord(entry, kv); if (permissionsOfUserOnTable != null) { String username = permissionsOfUserOnTable.getFirst(); TablePermission permissions = permissionsOfUserOnTable.getSecond(); perms.put(username, permissions); } } - if (table != null) { - allPerms.put(table, perms); + if (entry != null) { + allPerms.put(entry, perms); } if (!hasNext) { break; @@ -371,10 +404,10 @@ public class AccessControlLists { * Load all permissions from the region server holding {@code _acl_}, * primarily intended for testing purposes. */ - static Map> loadAll( + static Map> loadAll( Configuration conf) throws IOException { - Map> allPerms = - new TreeMap>(); + Map> allPerms = + new TreeMap>(Bytes.BYTES_RAWCOMPARATOR); // do a full scan of _acl_, filtering on only first table region rows @@ -387,10 +420,9 @@ public class AccessControlLists { acls = new HTable(conf, ACL_TABLE_NAME); scanner = acls.getScanner(scan); for (Result row : scanner) { - TableName tableName = TableName.valueOf(row.getRow()); ListMultimap resultPerms = - parseTablePermissions(tableName, row); - allPerms.put(tableName, resultPerms); + parsePermissions(row.getRow(), row); + allPerms.put(row.getRow(), resultPerms); } } finally { if (scanner != null) scanner.close(); @@ -400,6 +432,16 @@ public class AccessControlLists { return allPerms; } + static ListMultimap getTablePermissions(Configuration conf, + TableName tableName) throws IOException { + return getPermissions(conf, tableName != null ? tableName.getName() : null); + } + + static ListMultimap getNamespacePermissions(Configuration conf, + String namespace) throws IOException { + return getPermissions(conf, Bytes.toBytes(toNamespaceEntry(namespace))); + } + /** * Reads user permission assignments stored in the l: column * family of the first table row in _acl_. @@ -409,23 +451,23 @@ public class AccessControlLists { * used for storage. *

*/ - static ListMultimap getTablePermissions(Configuration conf, - TableName tableName) throws IOException { - if (tableName == null) tableName = ACL_TABLE; + static ListMultimap getPermissions(Configuration conf, + byte[] entryName) throws IOException { + if (entryName == null) entryName = ACL_TABLE_NAME.getName(); // for normal user tables, we just read the table row from _acl_ ListMultimap perms = ArrayListMultimap.create(); HTable acls = null; try { - acls = new HTable(conf, ACL_TABLE); - Get get = new Get(tableName.getName()); + acls = new HTable(conf, ACL_TABLE_NAME); + Get get = new Get(entryName); get.addFamily(ACL_LIST_FAMILY); Result row = acls.get(get); if (!row.isEmpty()) { - perms = parseTablePermissions(tableName, row); + perms = parsePermissions(entryName, row); } else { - LOG.info("No permissions found in " + ACL_TABLE_NAME_STR + " for table " - + tableName); + LOG.info("No permissions found in " + ACL_TABLE_NAME + " for acl entry " + + Bytes.toString(entryName)); } } finally { if (acls != null) acls.close(); @@ -438,11 +480,21 @@ public class AccessControlLists { * Returns the currently granted permissions for a given table as a list of * user plus associated permissions. */ + static List getUserTablePermissions( + Configuration conf, TableName tableName) throws IOException { + return getUserPermissions(conf, tableName.getName()); + } + + static List getUserNamespacePermissions( + Configuration conf, String namespace) throws IOException { + return getUserPermissions(conf, Bytes.toBytes(toNamespaceEntry(namespace))); + } + static List getUserPermissions( - Configuration conf, TableName tableName) + Configuration conf, byte[] entryName) throws IOException { - ListMultimap allPerms = getTablePermissions( - conf, tableName); + ListMultimap allPerms = getPermissions( + conf, entryName); List perms = new ArrayList(); @@ -455,14 +507,14 @@ public class AccessControlLists { return perms; } - private static ListMultimap parseTablePermissions( - TableName table, Result result) { - ListMultimap perms = ArrayListMultimap.create(); + private static ListMultimap parsePermissions( + byte[] entryName, Result result) { + ListMultimap perms = ArrayListMultimap.create(); if (result != null && result.size() > 0) { for (KeyValue kv : result.raw()) { Pair permissionsOfUserOnTable = - parseTablePermissionRecord(table, kv); + parsePermissionRecord(entryName, kv); if (permissionsOfUserOnTable != null) { String username = permissionsOfUserOnTable.getFirst(); @@ -474,8 +526,8 @@ public class AccessControlLists { return perms; } - private static Pair parseTablePermissionRecord( - TableName table, KeyValue kv) { + private static Pair parsePermissionRecord( + byte[] entryName, KeyValue kv) { // return X given a set of permissions encoded in the permissionRecord kv. byte[] family = kv.getFamily(); @@ -494,6 +546,15 @@ public class AccessControlLists { // check for a column family appended to the key // TODO: avoid the string conversion to make this more efficient String username = Bytes.toString(key); + + //Handle namespace entry + if(isNamespaceEntry(entryName)) { + return new Pair(username, + new TablePermission(Bytes.toString(fromNamespaceEntry(entryName)), value)); + } + + //Handle table and global entry + //TODO global entry should be handled differently int idx = username.indexOf(ACL_KEY_DELIMITER); byte[] permFamily = null; byte[] permQualifier = null; @@ -509,8 +570,8 @@ public class AccessControlLists { } } - return new Pair( - username, new TablePermission(table, permFamily, permQualifier, value)); + return new Pair(username, + new TablePermission(TableName.valueOf(entryName), permFamily, permQualifier, value)); } /** @@ -534,8 +595,8 @@ public class AccessControlLists { if (ProtobufUtil.isPBMagicPrefix(data)) { int pblen = ProtobufUtil.lengthOfPBMagic(); try { - AccessControlProtos.UserTablePermissions perms = - AccessControlProtos.UserTablePermissions.newBuilder().mergeFrom( + AccessControlProtos.UsersAndPermissions perms = + AccessControlProtos.UsersAndPermissions.newBuilder().mergeFrom( data, pblen, data.length - pblen).build(); return ProtobufUtil.toUserTablePermissions(perms); } catch (InvalidProtocolBufferException e) { @@ -579,4 +640,37 @@ public class AccessControlLists { return aclKey.substring(GROUP_PREFIX.length()); } + + public static boolean isNamespaceEntry(String entryName) { + return entryName.charAt(0) == NAMESPACE_PREFIX; + } + + public static boolean isNamespaceEntry(byte[] entryName) { + return entryName[0] == NAMESPACE_PREFIX; + } + + public static String toNamespaceEntry(String namespace) { + return NAMESPACE_PREFIX + namespace; + } + + public static String fromNamespaceEntry(String namespace) { + if(namespace.charAt(0) != NAMESPACE_PREFIX) + throw new IllegalArgumentException("Argument is not a valid namespace entry"); + return namespace.substring(1); + } + + public static byte[] toNamespaceEntry(byte[] namespace) { + byte[] ret = new byte[namespace.length+1]; + ret[0] = NAMESPACE_PREFIX; + System.arraycopy(namespace, 0, ret, 1, namespace.length); + return ret; + } + + public static byte[] fromNamespaceEntry(byte[] namespace) { + if(namespace[0] != NAMESPACE_PREFIX) { + throw new IllegalArgumentException("Argument is not a valid namespace entry: " + + Bytes.toString(namespace)); + } + return Arrays.copyOfRange(namespace, 1, namespace.length); + } } Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java (original) +++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java Tue Aug 13 21:49:56 2013 @@ -26,6 +26,8 @@ import java.util.TreeSet; import com.google.protobuf.RpcCallback; import com.google.protobuf.RpcController; import com.google.protobuf.Service; + +import org.apache.commons.lang.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; @@ -139,16 +141,16 @@ public class AccessController extends Ba void initialize(RegionCoprocessorEnvironment e) throws IOException { final HRegion region = e.getRegion(); - Map> tables = + Map> tables = AccessControlLists.loadAll(region); // For each table, write out the table's permissions to the respective // znode for that table. - for (Map.Entry> t: + for (Map.Entry> t: tables.entrySet()) { - TableName table = t.getKey(); + byte[] entry = t.getKey(); ListMultimap perms = t.getValue(); byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, e.getConfiguration()); - this.authManager.getZKPermissionWatcher().writeToZookeeper(table, serialized); + this.authManager.getZKPermissionWatcher().writeToZookeeper(entry, serialized); } } @@ -159,8 +161,8 @@ public class AccessController extends Ba */ void updateACL(RegionCoprocessorEnvironment e, final Map> familyMap) { - Set tableSet = - new TreeSet(); + Set entries = + new TreeSet(Bytes.BYTES_RAWCOMPARATOR); for (Map.Entry> f : familyMap.entrySet()) { List cells = f.getValue(); for (Cell cell: cells) { @@ -168,21 +170,21 @@ public class AccessController extends Ba if (Bytes.equals(kv.getBuffer(), kv.getFamilyOffset(), kv.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0, AccessControlLists.ACL_LIST_FAMILY.length)) { - tableSet.add(TableName.valueOf(kv.getRow())); + entries.add(kv.getRow()); } } } - ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher(); Configuration conf = regionEnv.getConfiguration(); - for (TableName tableName: tableSet) { + for (byte[] entry: entries) { try { ListMultimap perms = - AccessControlLists.getTablePermissions(conf, tableName); + AccessControlLists.getPermissions(conf, entry); byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf); - zkw.writeToZookeeper(tableName, serialized); + zkw.writeToZookeeper(entry, serialized); } catch (IOException ex) { - LOG.error("Failed updating permissions mirror for '" + tableName + "'", ex); + LOG.error("Failed updating permissions mirror for '" + Bytes.toString(entry) + "'", + ex); } } } @@ -353,6 +355,35 @@ public class AccessController extends Ba } /** + * Authorizes that the current user has any of the given permissions for the + * given table, column family and column qualifier. + * @param namespace + * @throws IOException if obtaining the current user fails + * @throws AccessDeniedException if user has no authorization + */ + private void requirePermission(String request, String namespace, + Action... permissions) throws IOException { + User user = getActiveUser(); + AuthResult result = null; + + for (Action permission : permissions) { + if (authManager.authorize(user, namespace, permission)) { + result = AuthResult.allow(request, "Table permission granted", user, + permission, namespace); + break; + } else { + // rest of the world + result = AuthResult.deny(request, "Insufficient permissions", user, + permission, namespace); + } + } + logResult(result); + if (!result.isAllowed()) { + throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); + } + } + + /** * Authorizes that the current user has global privileges for the given action. * @param perm The action being requested * @throws IOException if obtaining the current user fails @@ -393,7 +424,7 @@ public class AccessController extends Ba * being authorized, based on the given parameters. * @param perm Action being requested * @param tableName Affected table name. - * @param familiMap Affected column families. + * @param familyMap Affected column families. */ private void requireGlobalPermission(String request, Permission.Action perm, TableName tableName, Map> familyMap) throws IOException { @@ -409,6 +440,26 @@ public class AccessController extends Ba } /** + * Checks that the user has the given global permission. The generated + * audit log message will contain context information for the operation + * being authorized, based on the given parameters. + * @param perm Action being requested + * @param namespace + */ + private void requireGlobalPermission(String request, Permission.Action perm, + String namespace) throws IOException { + User user = getActiveUser(); + if (authManager.authorize(user, perm)) { + logResult(AuthResult.allow(request, "Global check allowed", user, perm, namespace)); + } else { + logResult(AuthResult.deny(request, "Global check failed", user, perm, namespace)); + throw new AccessDeniedException("Insufficient permissions for user '" + + (user != null ? user.getShortName() : "null") +"' (global, action=" + + perm.toString() + ")"); + } + } + + /** * Returns true if the current user is allowed the given action * over at least one of the column qualifiers in the given column families. */ @@ -632,7 +683,7 @@ public class AccessController extends Ba throws IOException { if (Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) { throw new AccessDeniedException("Not allowed to disable " - + AccessControlLists.ACL_TABLE_NAME_STR + " table."); + + AccessControlLists.ACL_TABLE_NAME + " table."); } requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE); } @@ -779,27 +830,33 @@ public class AccessController extends Ba @Override public void preCreateNamespace(ObserverContext ctx, - NamespaceDescriptor ns) throws IOException { + NamespaceDescriptor ns) throws IOException { + requireGlobalPermission("createNamespace", Action.ADMIN, ns.getName()); } @Override public void postCreateNamespace(ObserverContext ctx, - NamespaceDescriptor ns) throws IOException { + NamespaceDescriptor ns) throws IOException { } @Override - public void preDeleteNamespace(ObserverContext ctx, - String namespace) throws IOException { + public void preDeleteNamespace(ObserverContext ctx, String namespace) + throws IOException { + requireGlobalPermission("deleteNamespace", Action.ADMIN, namespace); } @Override public void postDeleteNamespace(ObserverContext ctx, String namespace) throws IOException { + AccessControlLists.removeNamespacePermissions(ctx.getEnvironment().getConfiguration(), + namespace); + LOG.info(namespace + "entry deleted in "+AccessControlLists.ACL_TABLE_NAME+" table."); } @Override public void preModifyNamespace(ObserverContext ctx, - NamespaceDescriptor ns) throws IOException { + NamespaceDescriptor ns) throws IOException { + requireGlobalPermission("modifyNamespace", Action.ADMIN, ns.getName()); } @Override @@ -1139,7 +1196,7 @@ public class AccessController extends Ba action, e, Collections.EMPTY_MAP); if (!authResult.isAllowed()) { for(UserPermission userPerm: - AccessControlLists.getUserPermissions(regionEnv.getConfiguration(), tableName)) { + AccessControlLists.getUserTablePermissions(regionEnv.getConfiguration(), tableName)) { for(Permission.Action userAction: userPerm.getActions()) { if(userAction.equals(action)) { return AuthResult.allow(method, "Access allowed", requestUser, @@ -1188,7 +1245,7 @@ public class AccessController extends Ba public void grant(RpcController controller, AccessControlProtos.GrantRequest request, RpcCallback done) { - UserPermission perm = ProtobufUtil.toUserPermission(request.getPermission()); + UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission()); AccessControlProtos.GrantResponse response = null; try { // verify it's only running at .acl. @@ -1197,7 +1254,15 @@ public class AccessController extends Ba LOG.debug("Received request to grant access permission " + perm.toString()); } - requirePermission("grant", perm.getTable(), perm.getFamily(), perm.getQualifier(), Action.ADMIN); + switch(request.getUserPermission().getPermission().getType()) { + case Global : + case Table : + requirePermission("grant", perm.getTable(), perm.getFamily(), + perm.getQualifier(), Action.ADMIN); + break; + case Namespace : + requireGlobalPermission("grant", Action.ADMIN, perm.getNamespace()); + } AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm); if (AUDITLOG.isTraceEnabled()) { @@ -1206,7 +1271,7 @@ public class AccessController extends Ba } } else { throw new CoprocessorException(AccessController.class, "This method " - + "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table."); + + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table."); } response = AccessControlProtos.GrantResponse.getDefaultInstance(); } catch (IOException ioe) { @@ -1220,7 +1285,7 @@ public class AccessController extends Ba public void revoke(RpcController controller, AccessControlProtos.RevokeRequest request, RpcCallback done) { - UserPermission perm = ProtobufUtil.toUserPermission(request.getPermission()); + UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission()); AccessControlProtos.RevokeResponse response = null; try { // only allowed to be called on _acl_ region @@ -1229,8 +1294,15 @@ public class AccessController extends Ba LOG.debug("Received request to revoke access permission " + perm.toString()); } - requirePermission("revoke", perm.getTable(), perm.getFamily(), - perm.getQualifier(), Action.ADMIN); + switch(request.getUserPermission().getPermission().getType()) { + case Global : + case Table : + requirePermission("revoke", perm.getTable(), perm.getFamily(), + perm.getQualifier(), Action.ADMIN); + break; + case Namespace : + requireGlobalPermission("revoke", Action.ADMIN, perm.getNamespace()); + } AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm); if (AUDITLOG.isTraceEnabled()) { @@ -1239,7 +1311,7 @@ public class AccessController extends Ba } } else { throw new CoprocessorException(AccessController.class, "This method " - + "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table."); + + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table."); } response = AccessControlProtos.RevokeResponse.getDefaultInstance(); } catch (IOException ioe) { @@ -1254,21 +1326,30 @@ public class AccessController extends Ba AccessControlProtos.UserPermissionsRequest request, RpcCallback done) { AccessControlProtos.UserPermissionsResponse response = null; - TableName table = null; - if (request.hasTableName()) { - table = ProtobufUtil.toTableName(request.getTableName()); - } try { // only allowed to be called on _acl_ region if (aclRegion) { - requirePermission("userPermissions", table, null, null, Action.ADMIN); + List perms = null; + if(request.getType() == AccessControlProtos.Permission.Type.Table) { + TableName table = null; + if (request.hasTableName()) { + table = ProtobufUtil.toTableName(request.getTableName()); + } + requirePermission("userPermissions", table, null, null, Action.ADMIN); - List perms = AccessControlLists.getUserPermissions( - regionEnv.getConfiguration(), table); + perms = AccessControlLists.getUserTablePermissions( + regionEnv.getConfiguration(), table); + } else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) { + perms = AccessControlLists.getUserNamespacePermissions( + regionEnv.getConfiguration(), request.getNamespaceName().toStringUtf8()); + } else { + perms = AccessControlLists.getUserPermissions( + regionEnv.getConfiguration(), null); + } response = ResponseConverter.buildUserPermissionsResponse(perms); } else { throw new CoprocessorException(AccessController.class, "This method " - + "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table."); + + "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table."); } } catch (IOException ioe) { // pass exception back up @@ -1371,7 +1452,8 @@ public class AccessController extends Ba private boolean isSpecialTable(HRegionInfo regionInfo) { TableName tableName = regionInfo.getTableName(); - return tableName.equals(AccessControlLists.ACL_TABLE) + return tableName.equals(AccessControlLists.ACL_TABLE_NAME) + || tableName.equals(TableName.NAMESPACE_TABLE_NAME) || tableName.equals(TableName.META_TABLE_NAME); } Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java (original) +++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java Tue Aug 13 21:49:56 2013 @@ -36,6 +36,7 @@ import org.apache.hadoop.hbase.util.Byte @InterfaceStability.Evolving public class AuthResult { private final boolean allowed; + private final String namespace; private final TableName table; private final Permission.Action action; private final String request; @@ -58,6 +59,7 @@ public class AuthResult { this.qualifier = qualifier; this.action = action; this.families = null; + this.namespace = null; } public AuthResult(boolean allowed, String request, String reason, User user, @@ -72,6 +74,21 @@ public class AuthResult { this.qualifier = null; this.action = action; this.families = families; + this.namespace = null; + } + + public AuthResult(boolean allowed, String request, String reason, User user, + Permission.Action action, String namespace) { + this.allowed = allowed; + this.request = request; + this.reason = reason; + this.user = user; + this.namespace = namespace; + this.action = action; + this.table = null; + this.family = null; + this.qualifier = null; + this.families = null; } public boolean isAllowed() { @@ -153,11 +170,13 @@ public class AuthResult { .append(user != null ? user.getName() : "UNKNOWN") .append(", "); sb.append("scope=") - .append(table == null ? "GLOBAL" : table) + .append(namespace != null ? namespace : table == null ? "GLOBAL" : table); + if(namespace == null) { + sb.append(", ") + .append("family=") + .append(toFamilyString()) .append(", "); - sb.append("family=") - .append(toFamilyString()) - .append(", "); + } sb.append("action=") .append(action != null ? action.toString() : "") .append(")"); @@ -169,6 +188,11 @@ public class AuthResult { } public static AuthResult allow(String request, String reason, User user, + Permission.Action action, String namespace) { + return new AuthResult(true, request, reason, user, action, namespace); + } + + public static AuthResult allow(String request, String reason, User user, Permission.Action action, TableName table, byte[] family, byte[] qualifier) { return new AuthResult(true, request, reason, user, action, table, family, qualifier); } @@ -180,6 +204,11 @@ public class AuthResult { } public static AuthResult deny(String request, String reason, User user, + Permission.Action action, String namespace) { + return new AuthResult(false, request, reason, user, action, namespace); + } + + public static AuthResult deny(String request, String reason, User user, Permission.Action action, TableName table, byte[] family, byte[] qualifier) { return new AuthResult(false, request, reason, user, action, table, family, qualifier); } Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java (original) +++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java Tue Aug 13 21:49:56 2013 @@ -96,6 +96,9 @@ public class TableAuthManager { private ConcurrentSkipListMap> tableCache = new ConcurrentSkipListMap>(); + private ConcurrentSkipListMap> nsCache = + new ConcurrentSkipListMap>(); + private Configuration conf; private ZKPermissionWatcher zkperms; @@ -147,7 +150,7 @@ public class TableAuthManager { return this.zkperms; } - public void refreshCacheFromWritable(TableName table, + public void refreshTableCacheFromWritable(TableName table, byte[] data) throws IOException { if (data != null && data.length > 0) { ListMultimap perms; @@ -169,6 +172,22 @@ public class TableAuthManager { } } + public void refreshNamespaceCacheFromWritable(String namespace, byte[] data) throws IOException { + if (data != null && data.length > 0) { + ListMultimap perms; + try { + perms = AccessControlLists.readPermissions(data, conf); + } catch (DeserializationException e) { + throw new IOException(e); + } + if (perms != null) { + updateNsCache(namespace, perms); + } + } else { + LOG.debug("Skipping permission cache refresh because writable data is empty"); + } + } + /** * Updates the internal global permissions cache * @@ -216,6 +235,29 @@ public class TableAuthManager { tableCache.put(table, newTablePerms); } + /** + * Updates the internal permissions cache for a single table, splitting + * the permissions listed into separate caches for users and groups to optimize + * group lookups. + * + * @param namespace + * @param tablePerms + */ + private void updateNsCache(String namespace, + ListMultimap tablePerms) { + PermissionCache newTablePerms = new PermissionCache(); + + for (Map.Entry entry : tablePerms.entries()) { + if (AccessControlLists.isGroupPrincipal(entry.getKey())) { + newTablePerms.putGroup(AccessControlLists.getGroupName(entry.getKey()), entry.getValue()); + } else { + newTablePerms.putUser(entry.getKey(), entry.getValue()); + } + } + + nsCache.put(namespace, newTablePerms); + } + private PermissionCache getTablePermissions(TableName table) { if (!tableCache.containsKey(table)) { tableCache.putIfAbsent(table, new PermissionCache()); @@ -223,6 +265,13 @@ public class TableAuthManager { return tableCache.get(table); } + private PermissionCache getNamespacePermissions(String namespace) { + if (!nsCache.containsKey(namespace)) { + nsCache.putIfAbsent(namespace, new PermissionCache()); + } + return nsCache.get(namespace); + } + /** * Authorizes a global permission * @param perms @@ -329,6 +378,45 @@ public class TableAuthManager { return false; } + public boolean authorize(User user, String namespace, Permission.Action action) { + if (authorizeUser(user.getShortName(), action)) { + return true; + } + PermissionCache tablePerms = nsCache.get(namespace); + if (tablePerms != null) { + List userPerms = tablePerms.getUser(user.getShortName()); + if (authorize(userPerms, namespace, action)) { + return true; + } + + String[] groupNames = user.getGroupNames(); + if (groupNames != null) { + for (String group : groupNames) { + List groupPerms = tablePerms.getGroup(group); + if (authorize(groupPerms, namespace, action)) { + return true; + } + } + } + } + return false; + } + + private boolean authorize(List perms, String namespace, + Permission.Action action) { + if (perms != null) { + for (TablePermission p : perms) { + if (p.implies(namespace, action)) { + return true; + } + } + } else if (LOG.isDebugEnabled()) { + LOG.debug("No permissions for authorize() check, table=" + namespace); + } + + return false; + } + /** * Checks global authorization for a specific action for a user, based on the * stored user permissions. @@ -358,7 +446,7 @@ public class TableAuthManager { if (authorizeUser(username, action)) { return true; } - if (table == null) table = AccessControlLists.ACL_TABLE; + if (table == null) table = AccessControlLists.ACL_TABLE_NAME; return authorize(getTablePermissions(table).getUser(username), table, family, qualifier, action); } @@ -387,7 +475,7 @@ public class TableAuthManager { if (authorizeGroup(groupName, action)) { return true; } - if (table == null) table = AccessControlLists.ACL_TABLE; + if (table == null) table = AccessControlLists.ACL_TABLE_NAME; return authorize(getTablePermissions(table).getGroup(groupName), table, family, action); } @@ -481,11 +569,11 @@ public class TableAuthManager { return false; } - public void remove(byte[] table) { - remove(TableName.valueOf(table)); + public void removeNamespace(byte[] ns) { + nsCache.remove(ns); } - public void remove(TableName table) { + public void removeTable(TableName table) { tableCache.remove(table); } @@ -496,11 +584,11 @@ public class TableAuthManager { * @param table * @param perms */ - public void setUserPermissions(String username, TableName table, + public void setTableUserPermissions(String username, TableName table, List perms) { PermissionCache tablePerms = getTablePermissions(table); tablePerms.replaceUser(username, perms); - writeToZooKeeper(table, tablePerms); + writeTableToZooKeeper(table, tablePerms); } /** @@ -510,20 +598,58 @@ public class TableAuthManager { * @param table * @param perms */ - public void setGroupPermissions(String group, TableName table, + public void setTableGroupPermissions(String group, TableName table, List perms) { PermissionCache tablePerms = getTablePermissions(table); tablePerms.replaceGroup(group, perms); - writeToZooKeeper(table, tablePerms); + writeTableToZooKeeper(table, tablePerms); + } + + /** + * Overwrites the existing permission set for a given user for a table, and + * triggers an update for zookeeper synchronization. + * @param username + * @param namespace + * @param perms + */ + public void setNamespaceUserPermissions(String username, String namespace, + List perms) { + PermissionCache tablePerms = getNamespacePermissions(namespace); + tablePerms.replaceUser(username, perms); + writeNamespaceToZooKeeper(namespace, tablePerms); + } + + /** + * Overwrites the existing permission set for a group and triggers an update + * for zookeeper synchronization. + * @param group + * @param namespace + * @param perms + */ + public void setNamespaceGroupPermissions(String group, String namespace, + List perms) { + PermissionCache tablePerms = getNamespacePermissions(namespace); + tablePerms.replaceGroup(group, perms); + writeNamespaceToZooKeeper(namespace, tablePerms); + } + + public void writeTableToZooKeeper(TableName table, + PermissionCache tablePerms) { + byte[] serialized = new byte[0]; + if (tablePerms != null) { + serialized = AccessControlLists.writePermissionsAsBytes(tablePerms.getAllPermissions(), conf); + } + zkperms.writeToZookeeper(table.getName(), serialized); } - public void writeToZooKeeper(TableName table, + public void writeNamespaceToZooKeeper(String namespace, PermissionCache tablePerms) { byte[] serialized = new byte[0]; if (tablePerms != null) { serialized = AccessControlLists.writePermissionsAsBytes(tablePerms.getAllPermissions(), conf); } - zkperms.writeToZookeeper(table, serialized); + zkperms.writeToZookeeper(Bytes.toBytes(AccessControlLists.toNamespaceEntry(namespace)), + serialized); } static Map managerMap = Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java (original) +++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java Tue Aug 13 21:49:56 2013 @@ -85,7 +85,11 @@ public class ZKPermissionWatcher extends public void nodeDeleted(String path) { if (aclZNode.equals(ZKUtil.getParent(path))) { String table = ZKUtil.getNodeName(path); - authManager.remove(Bytes.toBytes(table)); + if(AccessControlLists.isNamespaceEntry(table)) { + authManager.removeNamespace(Bytes.toBytes(table)); + } else { + authManager.removeTable(TableName.valueOf(table)); + } } } @@ -93,14 +97,14 @@ public class ZKPermissionWatcher extends public void nodeDataChanged(String path) { if (aclZNode.equals(ZKUtil.getParent(path))) { // update cache on an existing table node - TableName table = TableName.valueOf(ZKUtil.getNodeName(path)); + String entry = ZKUtil.getNodeName(path); try { byte[] data = ZKUtil.getDataAndWatch(watcher, path); - authManager.refreshCacheFromWritable(table, data); + refreshAuthManager(entry, data); } catch (KeeperException ke) { - LOG.error("Error reading data from zookeeper for node "+table, ke); + LOG.error("Error reading data from zookeeper for node " + entry, ke); // only option is to abort - watcher.abort("Zookeeper error getting data for node " + table, ke); + watcher.abort("Zookeeper error getting data for node " + entry, ke); } catch (IOException ioe) { LOG.error("Error reading permissions writables", ioe); } @@ -126,36 +130,45 @@ public class ZKPermissionWatcher extends for (ZKUtil.NodeAndData n : nodes) { if (n.isEmpty()) continue; String path = n.getNode(); - TableName table = TableName.valueOf(ZKUtil.getNodeName(path)); + String entry = (ZKUtil.getNodeName(path)); try { - byte[] nodeData = n.getData(); - if (LOG.isDebugEnabled()) { - LOG.debug("Updating permissions cache from node "+table+" with data: "+ - Bytes.toStringBinary(nodeData)); - } - authManager.refreshCacheFromWritable(table, nodeData); + refreshAuthManager(entry, n.getData()); } catch (IOException ioe) { - LOG.error("Failed parsing permissions for table '" + table + + LOG.error("Failed parsing permissions for table '" + entry + "' from zk", ioe); } } } + private void refreshAuthManager(String entry, byte[] nodeData) throws IOException { + if (LOG.isDebugEnabled()) { + LOG.debug("Updating permissions cache from node "+entry+" with data: "+ + Bytes.toStringBinary(nodeData)); + } + if(AccessControlLists.isNamespaceEntry(entry)) { + authManager.refreshNamespaceCacheFromWritable( + AccessControlLists.fromNamespaceEntry(entry), nodeData); + } else { + authManager.refreshTableCacheFromWritable(TableName.valueOf(entry), nodeData); + } + } + /*** * Write a table's access controls to the permissions mirror in zookeeper - * @param tableName + * @param entry * @param permsData */ - public void writeToZookeeper(TableName tableName, byte[] permsData) { + public void writeToZookeeper(byte[] entry, byte[] permsData) { + String entryName = Bytes.toString(entry); String zkNode = ZKUtil.joinZNode(watcher.baseZNode, ACL_NODE); - zkNode = ZKUtil.joinZNode(zkNode, tableName.getNameAsString()); + zkNode = ZKUtil.joinZNode(zkNode, entryName); try { ZKUtil.createWithParents(watcher, zkNode); ZKUtil.updateExistingNodeData(watcher, zkNode, permsData, -1); } catch (KeeperException e) { - LOG.error("Failed updating permissions for table '" + - tableName + "'", e); + LOG.error("Failed updating permissions for entry '" + + entryName + "'", e); watcher.abort("Failed writing node "+zkNode+" to zookeeper", e); } } Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/thrift/generated/Hbase.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/thrift/generated/Hbase.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/thrift/generated/Hbase.java (original) +++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/thrift/generated/Hbase.java Tue Aug 13 21:49:56 2013 @@ -3822,7 +3822,8 @@ public class Hbase { } } - public static class createTable extends org.apache.thrift.ProcessFunction { + public static class + createTable extends org.apache.thrift.ProcessFunction { public createTable() { super("createTable"); } Modified: hbase/trunk/hbase-server/src/main/ruby/hbase/security.rb URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/ruby/hbase/security.rb?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/main/ruby/hbase/security.rb (original) +++ hbase/trunk/hbase-server/src/main/ruby/hbase/security.rb Tue Aug 13 21:49:56 2013 @@ -36,26 +36,6 @@ module Hbase # TODO: need to validate user name - # Verify that the specified permission is valid - if (permissions == nil || permissions.length == 0) - raise(ArgumentError, "Invalid permission: no actions associated with user") - end - - if (table_name != nil) - # Table should exist - raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name) - - tablebytes=table_name.to_java_bytes - htd = @admin.getTableDescriptor(tablebytes) - - if (family != nil) - raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes) - end - - fambytes = family.to_java_bytes if (family != nil) - qualbytes = qualifier.to_java_bytes if (qualifier != nil) - end - begin meta_table = org.apache.hadoop.hbase.client.HTable.new(@config, org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME) @@ -67,10 +47,48 @@ module Hbase perm = org.apache.hadoop.hbase.security.access.Permission.new( permissions.to_java_bytes) - # invoke cp endpoint to perform access controlse - org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant( - protocol, user, tablebytes, fambytes, - qualbytes, perm.getActions()) + # Verify that the specified permission is valid + if (permissions == nil || permissions.length == 0) + raise(ArgumentError, "Invalid permission: no actions associated with user") + end + + if (table_name != nil) + #check if the tablename passed is actually a namespace + if (isNamespace?(table_name)) + # Namespace should exist first. + namespace_name = table_name[1...table_name.length] + raise(ArgumentError, "Can't find a namespace: #{namespace_name}") unless namespace_exists?(namespace_name) + + #We pass the namespace name along with "@" so that we can differentiate a namespace from a table. + tablebytes=table_name.to_java_bytes + # invoke cp endpoint to perform access controlse + org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant( + protocol, user, tablebytes, perm.getActions()) + else + # Table should exist + raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name) + + tableName = org.apache.hadoop.hbase.TableName.valueOf(table_name.to_java_bytes) + htd = @admin.getTableDescriptor(tablebytes) + + if (family != nil) + raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes) + end + + fambytes = family.to_java_bytes if (family != nil) + qualbytes = qualifier.to_java_bytes if (qualifier != nil) + + # invoke cp endpoint to perform access controlse + org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant( + protocol, user, tableName, fambytes, + qualbytes, perm.getActions()) + end + else + # invoke cp endpoint to perform access controlse + org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant( + protocol, user, perm.getActions()) + end + ensure meta_table.close() end @@ -82,21 +100,6 @@ module Hbase # TODO: need to validate user name - if (table_name != nil) - # Table should exist - raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name) - - tablebytes=table_name.to_java_bytes - htd = @admin.getTableDescriptor(tablebytes) - - if (family != nil) - raise(ArgumentError, "Can't find family: #{family}") unless htd.hasFamily(family.to_java_bytes) - end - - fambytes = family.to_java_bytes if (family != nil) - qualbytes = qualifier.to_java_bytes if (qualifier != nil) - end - begin meta_table = org.apache.hadoop.hbase.client.HTable.new(@config, org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME) @@ -106,9 +109,41 @@ module Hbase protocol = org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos:: AccessControlService.newBlockingStub(service) - # invoke cp endpoint to perform access controlse - org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke( - protocol, user, tablebytes, fambytes, qualbytes) + if (table_name != nil) + #check if the tablename passed is actually a namespace + if (isNamespace?(table_name)) + # Namespace should exist first. + namespace_name = table_name[1...table_name.length] + raise(ArgumentError, "Can't find a namespace: #{namespace_name}") unless namespace_exists?(namespace_name) + + #We pass the namespace name along with "@" so that we can differentiate a namespace from a table. + tablebytes=table_name.to_java_bytes + # invoke cp endpoint to perform access controlse + org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke( + protocol, user, tablebytes) + else + # Table should exist + raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name) + + tableName = org.apache.hadoop.hbase.TableName.valueOf(table_name.to_java_bytes) + htd = @admin.getTableDescriptor(tableName) + + if (family != nil) + raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes) + end + + fambytes = family.to_java_bytes if (family != nil) + qualbytes = qualifier.to_java_bytes if (qualifier != nil) + + # invoke cp endpoint to perform access controlse + org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke( + protocol, user, tableName, fambytes, qualbytes) + end + else + # invoke cp endpoint to perform access controlse + org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke( + protocol, user) + end ensure meta_table.close() end @@ -118,12 +153,6 @@ module Hbase def user_permission(table_name=nil) security_available? - if (table_name != nil) - raise(ArgumentError, "Can't find table: #{table_name}") unless exists?(table_name) - - tablebytes=table_name.to_java_bytes - end - begin meta_table = org.apache.hadoop.hbase.client.HTable.new(@config, org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME) @@ -133,9 +162,23 @@ module Hbase protocol = org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos:: AccessControlService.newBlockingStub(service) - # invoke cp endpoint to perform access controlse - perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions( - protocol, tablebytes) + if (table_name != nil) + #check if namespace is passed. + if (isNamespace?(table_name)) + # Namespace should exist first. + namespace_name = table_name[1...table_name.length] + raise(ArgumentError, "Can't find a namespace: #{namespace_name}") unless namespace_exists?(namespace_name) + # invoke cp endpoint to perform access controls + perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions( + protocol, table_name.to_java_bytes) + else + raise(ArgumentError, "Can't find table: #{table_name}") unless exists?(table_name) + perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions( + protocol, org.apache.hadoop.hbase.TableName.valueOf(table_name)) + end + else + perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions(protocol) + end ensure meta_table.close() end @@ -167,11 +210,24 @@ module Hbase @admin.tableExists(table_name) end + def isNamespace?(table_name) + table_name.start_with?('@') + end + + # Does Namespace exist + def namespace_exists?(namespace_name) + namespaceDesc = @admin.getNamespaceDescriptor(namespace_name) + if(namespaceDesc == nil) + return false + else + return true + end + end + # Make sure that security tables are available def security_available?() raise(ArgumentError, "DISABLED: Security features are not available") \ unless exists?(org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME) end - end end Modified: hbase/trunk/hbase-server/src/test/data/TestNamespaceUpgrade.tgz URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/data/TestNamespaceUpgrade.tgz?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== Binary files - no diff available. Modified: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFiles.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFiles.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFiles.java (original) +++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFiles.java Tue Aug 13 21:49:56 2013 @@ -49,7 +49,7 @@ public class TestSecureLoadIncrementalHF util.startMiniCluster(); // Wait for the ACL table to become available - util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME); + util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName()); } } Modified: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFilesSplitRecovery.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFilesSplitRecovery.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFilesSplitRecovery.java (original) +++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestSecureLoadIncrementalHFilesSplitRecovery.java Tue Aug 13 21:49:56 2013 @@ -54,7 +54,7 @@ public class TestSecureLoadIncrementalHF util.startMiniCluster(); // Wait for the ACL table to become available - util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME); + util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName()); } //Disabling this test as it does not work in secure mode Modified: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/migration/TestNamespaceUpgrade.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/migration/TestNamespaceUpgrade.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/migration/TestNamespaceUpgrade.java (original) +++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/migration/TestNamespaceUpgrade.java Tue Aug 13 21:49:56 2013 @@ -23,6 +23,7 @@ import static org.junit.Assert.*; import java.io.File; import java.io.IOException; +import java.util.List; import junit.framework.Assert; @@ -41,8 +42,11 @@ import org.apache.hadoop.hbase.TableName import org.apache.hadoop.hbase.Waiter; import org.apache.hadoop.hbase.client.HTable; 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.protobuf.generated.AdminProtos; +import org.apache.hadoop.hbase.regionserver.HRegion; +import org.apache.hadoop.hbase.security.access.AccessControlLists; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.FSUtils; import org.apache.hadoop.util.ToolRunner; @@ -64,6 +68,9 @@ import org.junit.experimental.categories * Contains snapshots with snapshot{num}Keys as the contents: * snapshot1Keys, snapshot2Keys * + * Image also contains _acl_ table with one region and two storefiles. + * This is needed to test the acl table migration. + * */ @Category(MediumTests.class) public class TestNamespaceUpgrade { @@ -103,6 +110,7 @@ public class TestNamespaceUpgrade { Configuration toolConf = TEST_UTIL.getConfiguration(); conf.set(HConstants.HBASE_DIR, TEST_UTIL.getDefaultRootDirPath().toString()); ToolRunner.run(toolConf, new NamespaceUpgrade(), new String[]{"--upgrade"}); + doFsCommand(shell, new String [] {"-lsr", "/"}); assertTrue(FSUtils.getVersion(fs, hbaseRootDir).equals(HConstants.FILE_SYSTEM_VERSION)); TEST_UTIL.startMiniHBaseCluster(1, 1); @@ -115,6 +123,22 @@ public class TestNamespaceUpgrade { Assert.assertEquals(currentKeys.length, count); } assertEquals(2, TEST_UTIL.getHBaseAdmin().listNamespaceDescriptors().length); + + //verify ACL table is migrated + HTable secureTable = new HTable(conf, AccessControlLists.ACL_TABLE_NAME); + ResultScanner scanner = secureTable.getScanner(new Scan()); + int count = 0; + for(Result r : scanner) { + count++; + } + assertEquals(3, count); + assertFalse(TEST_UTIL.getHBaseAdmin().tableExists("_acl_")); + + //verify ACL table was compacted + List regions = TEST_UTIL.getMiniHBaseCluster().getRegions(secureTable.getName()); + for(HRegion region : regions) { + assertEquals(1, region.getStores().size()); + } } private static File untar(final File testdir) throws IOException { Modified: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java (original) +++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java Tue Aug 13 21:49:56 2013 @@ -18,11 +18,25 @@ package org.apache.hadoop.hbase.security.access; +import static org.junit.Assert.fail; + import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException; +import org.apache.hadoop.hbase.protobuf.ProtobufUtil; +import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService; +import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest; +import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.User; +import com.google.protobuf.ServiceException; + /** * Utility methods for testing security */ @@ -37,4 +51,105 @@ public class SecureTestUtil { String currentUser = User.getCurrent().getName(); conf.set("hbase.superuser", "admin,"+currentUser); } + + public void verifyAllowed(User user, PrivilegedExceptionAction... actions) throws Exception { + for (PrivilegedExceptionAction action : actions) { + try { + user.runAs(action); + } catch (AccessDeniedException ade) { + fail("Expected action to pass for user '" + user.getShortName() + "' but was denied"); + } + } + } + + public void verifyAllowed(PrivilegedExceptionAction action, User... users) throws Exception { + for (User user : users) { + verifyAllowed(user, action); + } + } + + public void verifyDenied(User user, PrivilegedExceptionAction... actions) throws Exception { + for (PrivilegedExceptionAction action : actions) { + try { + user.runAs(action); + fail("Expected AccessDeniedException for user '" + user.getShortName() + "'"); + } catch (IOException e) { + boolean isAccessDeniedException = false; + if(e instanceof RetriesExhaustedWithDetailsException) { + // in case of batch operations, and put, the client assembles a + // RetriesExhaustedWithDetailsException instead of throwing an + // AccessDeniedException + for(Throwable ex : ((RetriesExhaustedWithDetailsException) e).getCauses()) { + if (ex instanceof AccessDeniedException) { + isAccessDeniedException = true; + break; + } + } + } + else { + // For doBulkLoad calls AccessDeniedException + // is buried in the stack trace + Throwable ex = e; + do { + if (ex instanceof AccessDeniedException) { + isAccessDeniedException = true; + break; + } + } while((ex = ex.getCause()) != null); + } + if (!isAccessDeniedException) { + fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'"); + } + } catch (UndeclaredThrowableException ute) { + // TODO why we get a PrivilegedActionException, which is unexpected? + Throwable ex = ute.getUndeclaredThrowable(); + if (ex instanceof PrivilegedActionException) { + ex = ((PrivilegedActionException) ex).getException(); + } + if (ex instanceof ServiceException) { + ServiceException se = (ServiceException)ex; + if (se.getCause() != null && se.getCause() instanceof AccessDeniedException) { + // expected result + return; + } + } + fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'"); + } + } + } + + public void verifyDenied(PrivilegedExceptionAction action, User... users) throws Exception { + for (User user : users) { + verifyDenied(user, action); + } + } + + public void checkTablePerms(Configuration conf, byte[] table, byte[] family, byte[] column, + Permission.Action... actions) throws IOException { + Permission[] perms = new Permission[actions.length]; + for (int i = 0; i < actions.length; i++) { + perms[i] = new TablePermission(TableName.valueOf(table), family, column, actions[i]); + } + + checkTablePerms(conf, table, perms); + } + + public void checkTablePerms(Configuration conf, byte[] table, Permission... perms) throws IOException { + CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder(); + for (Permission p : perms) { + request.addPermission(ProtobufUtil.toPermission(p)); + } + HTable acl = new HTable(conf, table); + try { + AccessControlService.BlockingInterface protocol = + AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0])); + try { + protocol.checkPermissions(null, request.build()); + } catch (ServiceException se) { + ProtobufUtil.toIOException(se); + } + } finally { + acl.close(); + } + } } Modified: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessControlFilter.java URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessControlFilter.java?rev=1513666&r1=1513665&r2=1513666&view=diff ============================================================================== --- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessControlFilter.java (original) +++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessControlFilter.java Tue Aug 13 21:49:56 2013 @@ -77,7 +77,7 @@ public class TestAccessControlFilter { conf.set("hbase.superuser", conf.get("hbase.superuser", "") + String.format(",%s.hfs.0,%s.hfs.1,%s.hfs.2", baseuser, baseuser, baseuser)); TEST_UTIL.startMiniCluster(); - TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME); + TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName()); ADMIN = User.createUserForTesting(conf, "admin", new String[]{"supergroup"}); READER = User.createUserForTesting(conf, "reader", new String[0]);