diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java index fcd9ebd4da..6c64e05808 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java @@ -51,6 +51,7 @@ public class RangerHadoopConstants { public static final String HBASE_UPDATE_RANGER_POLICIES_ON_GRANT_REVOKE_PROP = "xasecure.hbase.update.xapolicies.on.grant.revoke"; public static final boolean HBASE_UPDATE_RANGER_POLICIES_ON_GRANT_REVOKE_DEFAULT_VALUE = true; + public static final String HBASE_COLUMN_AUTH_OPTIMIZATION = "ranger.plugin.hbase.column.auth.optimization"; public static final String KNOX_ACCESS_VERIFIER_CLASS_NAME_PROP = "knox.authorization.verifier.classname"; public static final String KNOX_ACCESS_VERIFIER_CLASS_NAME_DEFAULT_VALUE = "org.apache.ranger.pdp.knox.RangerAuthorizer"; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/.RangerBasePlugin.java.swp b/agents-common/src/main/java/org/apache/ranger/plugin/service/.RangerBasePlugin.java.swp new file mode 100644 index 0000000000..7c8bb57460 Binary files /dev/null and b/agents-common/src/main/java/org/apache/ranger/plugin/service/.RangerBasePlugin.java.swp differ diff --git a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java index 6e999ba513..df68f571cb 100644 --- a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java +++ b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java @@ -30,11 +30,7 @@ import org.apache.hadoop.thirdparty.com.google.common.collect.Sets; import org.apache.hadoop.thirdparty.com.google.common.base.MoreObjects; import org.apache.ranger.audit.model.AuthzAuditEvent; -import org.apache.ranger.plugin.policyengine.RangerAccessRequest; -import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; -import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; -import org.apache.ranger.plugin.policyengine.RangerAccessResult; -import org.apache.ranger.plugin.service.RangerBasePlugin; +import org.apache.ranger.plugin.policyengine.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +43,7 @@ public class AuthorizationSession { final HbaseUserUtils _userUtils = _factory.getUserUtils(); final HbaseAuthUtils _authUtils = _factory.getAuthUtils(); // immutable state - final RangerBasePlugin _authorizer; + final RangerHBasePlugin _authorizer; // Mutable state: Use supplied state information String _operation; String _otherInformation; @@ -69,8 +65,8 @@ public class AuthorizationSession { // internal state per-authorization RangerAccessRequest _request; RangerAccessResult _result; - - public AuthorizationSession(RangerBasePlugin authorizer) { + + public AuthorizationSession(RangerHBasePlugin authorizer) { _authorizer = authorizer; } @@ -83,12 +79,12 @@ AuthorizationSession otherInformation(String information) { _otherInformation = information; return this; } - + AuthorizationSession remoteAddress(String ipAddress) { _remoteAddress = ipAddress; return this; } - + AuthorizationSession access(String anAccess) { _access = anAccess; return this; @@ -127,7 +123,7 @@ AuthorizationSession column(String aColumn) { } void verifyBuildable() { - + String template = "Internal error: Incomplete/inconsisten state: [%s]. Can't build auth request!"; if (_factory == null) { String message = String.format(template, "factory is null"); @@ -174,11 +170,7 @@ boolean isNameSpaceOperation() { StringUtils.equals(_operation, "getUserPermissionForNamespace"); } - AuthorizationSession buildRequest() { - - verifyBuildable(); - // session can be reused so reset its state - zapAuthorizationState(); + private RangerAccessResource createHBaseResource() { // TODO get this via a factory instead RangerAccessResourceImpl resource = new RangerHBaseResource(); // policy engine should deal sensibly with null/empty values, if any @@ -189,7 +181,11 @@ AuthorizationSession buildRequest() { } resource.setValue(RangerHBaseResource.KEY_COLUMN_FAMILY, _columnFamily); resource.setValue(RangerHBaseResource.KEY_COLUMN, _column); - + return resource; + } + + private RangerAccessRequest createRangerRequest() { + RangerAccessResource resource = createHBaseResource(); String user = _userUtils.getUserAsString(_user); RangerAccessRequestImpl request = new RangerAccessRequestImpl(resource, _access, user, _groups, null); request.setAction(_operation); @@ -198,18 +194,25 @@ AuthorizationSession buildRequest() { request.setResourceMatchingScope(_resourceMatchingScope); request.setAccessTime(new Date()); request.setIgnoreDescendantDeny(_ignoreDescendantDeny); - _request = request; + return request; + } + + AuthorizationSession buildRequest() { + verifyBuildable(); + // session can be reused so reset its state + zapAuthorizationState(); + _request = createRangerRequest(); if (LOG.isDebugEnabled()) { - LOG.debug("Built request: " + request.toString()); + LOG.debug("Built request: " + _request.toString()); } return this; } - + AuthorizationSession authorize() { if (LOG.isDebugEnabled()) { LOG.debug("==> AuthorizationSession.authorize: " + getRequestMessage()); } - + if (_request == null) { String message = String.format("Invalid state transition: buildRequest() must be called before authorize(). This request would ultimately get denied.!"); throw new IllegalStateException(message); @@ -223,7 +226,7 @@ AuthorizationSession authorize() { } _result = _authorizer.isAccessAllowed(_request, _auditHandler); } - + if (LOG.isDebugEnabled()) { boolean allowed = isAuthorized(); String reason = getDenialReason(); @@ -231,19 +234,19 @@ AuthorizationSession authorize() { } return this; } - + void logCapturedEvents() { if (_auditHandler != null) { List events = _auditHandler.getCapturedEvents(); _auditHandler.logAuthzAudits(events); } } - + void publishResults() throws AccessDeniedException { if (LOG.isDebugEnabled()) { LOG.debug("==> AuthorizationSession.publishResults()"); } - + boolean authorized = isAuthorized(); if (_auditHandler != null && isAudited()) { List events = null; @@ -284,7 +287,7 @@ void publishResults() throws AccessDeniedException { LOG.debug("<== AuthorizationSession.publishResults()"); } } - + boolean isAudited() { boolean audited = false; @@ -313,7 +316,7 @@ boolean isAuthorized() { } return allowed; } - + String getDenialReason() { String reason = ""; if (_result == null) { @@ -327,20 +330,21 @@ String getDenialReason() { } return reason; } - + String requestToString() { return MoreObjects.toStringHelper(_request.getClass()) - .add("operation", _operation) - .add("otherInformation", _otherInformation) - .add("access", _access) - .add("user", _user == null ? null : _user.getName()) - .add("groups", _groups) - .add("auditHandler", _auditHandler == null ? null : _auditHandler.getClass().getSimpleName()) - .add(RangerHBaseResource.KEY_TABLE, _table) - .add(RangerHBaseResource.KEY_COLUMN, _column) - .add(RangerHBaseResource.KEY_COLUMN_FAMILY, _columnFamily) - .add("resource-matching-scope", _resourceMatchingScope) - .toString(); + .add("operation", _operation) + .add("otherInformation", _otherInformation) + .add("access", _access) + .add("user", _user == null ? null : _user.getName()) + .add("groups", _groups) + .add("auditHandler", _auditHandler == null ? null : _auditHandler.getClass().getSimpleName()) + .add(RangerHBaseResource.KEY_TABLE, _table) + .add(RangerHBaseResource.KEY_COLUMN, _column) + .add(RangerHBaseResource.KEY_COLUMN_FAMILY, _columnFamily) + .add("resource-matching-scope", _resourceMatchingScope) + .add("ignoreDescendantDeny", _ignoreDescendantDeny) + .toString(); } String getPrintableValue(String value) { @@ -350,7 +354,7 @@ String getPrintableValue(String value) { return ""; } } - + String getRequestMessage() { String format = "Access[%s] by user[%s] belonging to groups[%s] to table[%s] for column-family[%s], column[%s] triggered by operation[%s], otherInformation[%s]"; String user = _userUtils.getUserAsString(); @@ -358,7 +362,7 @@ String getRequestMessage() { getPrintableValue(_columnFamily), getPrintableValue(_column), getPrintableValue(_operation), getPrintableValue(_otherInformation)); return message; } - + String getLogMessage(boolean allowed, String reason) { String format = " %s: status[%s], reason[%s]"; String message = String.format(format, getRequestMessage(), allowed ? "allowed" : "denied", reason); @@ -379,8 +383,13 @@ AuthorizationSession resourceMatchingScope(RangerAccessRequest.ResourceMatchingS _resourceMatchingScope = scope; return this; } + AuthorizationSession ignoreDescendantDeny(boolean ignoreDescendantDeny) { _ignoreDescendantDeny = ignoreDescendantDeny; return this; } -} + + public boolean getPropertyIsColumnAuthOptimizationEnabled() { + return _authorizer.getPropertyIsColumnAuthOptimizationEnabled(); + } +} \ No newline at end of file diff --git a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java index 2c9b6b80b4..57b3bb3553 100644 --- a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java +++ b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java @@ -76,6 +76,7 @@ import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; import org.apache.ranger.plugin.service.RangerBasePlugin; import org.apache.ranger.plugin.util.GrantRevokeRequest; +import org.apache.ranger.plugin.util.ServicePolicies; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,11 +91,11 @@ public class RangerAuthorizationCoprocessor implements AccessControlService.Inte private static final String GROUP_PREFIX = "@"; private UserProvider userProvider; - private RegionCoprocessorEnvironment regionEnv; + private RegionCoprocessorEnvironment regionEnv; private Map scannerOwners = new MapMaker().weakKeys().makeMap(); /** if we should check EXEC permissions */ private boolean shouldCheckExecPermission; - + /* * These are package level only for testability and aren't meant to be exposed outside via getters/setters or made available to derived classes. */ @@ -102,7 +103,16 @@ public class RangerAuthorizationCoprocessor implements AccessControlService.Inte final HbaseUserUtils _userUtils = _factory.getUserUtils(); final HbaseAuthUtils _authUtils = _factory.getAuthUtils(); private static volatile RangerHBasePlugin hbasePlugin = null; - + + public void setColumnAuthOptimizationEnabled(boolean enable) throws Exception { + if (hbasePlugin!=null) { + hbasePlugin.setColumnAuthOptimizationEnabled(enable); + } + else { + throw new Exception("Error while enabling column auth optimization"); + } + } + // Utilities Methods protected byte[] getTableName(RegionCoprocessorEnvironment e) { Region region = e.getRegion(); @@ -139,7 +149,7 @@ protected boolean isSpecialTable(String input) { return true; } } - + return false; } protected boolean isAccessForMetaTables(RegionCoprocessorEnvironment env) { @@ -205,7 +215,7 @@ private User getActiveUser(ObserverContext ctx) { return user; } - + private String getRemoteAddress() { InetAddress remoteAddr = null; try { @@ -237,7 +247,7 @@ private void requireScannerOwner(ObserverContext ctx, InternalScanner s) thro String owner = scannerOwners.get(s); if (owner != null && !owner.equals(requestUserName)) { throw new AccessDeniedException("User '"+ requestUserName +"' is not the scanner owner!"); - } + } } /** * @param families @@ -258,8 +268,16 @@ Map> getColumnFamilies(Map> Collection columnCollection = anEntry.getValue(); if (CollectionUtils.isEmpty(columnCollection)) { // family points to null map, OK. + // if column auth disabled, then also empty set is fine + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuthorizationCoprocessor getColumnFamilies: columns are empty. " + + "Setting columns to emptySet in familyMap"); + } result.put(family, Collections. emptySet()); } else { + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuthorizationCoprocessor getColumnFamilies: columns exist"); + } Iterator columnIterator = new ColumnIterator(columnCollection); Set columns = new HashSet(); try { @@ -278,7 +296,7 @@ Map> getColumnFamilies(Map> } return result; } - + static class ColumnFamilyAccessResult { final boolean _everythingIsAccessible; final boolean _somethingIsAccessible; @@ -301,7 +319,7 @@ static class ColumnFamilyAccessResult { // cached values of access results _filter = filter; } - + @Override public String toString() { return MoreObjects.toStringHelper(getClass()) @@ -313,12 +331,15 @@ public String toString() { .add("denialReason", _denialReason) .add("filter", _filter) .toString(); - + } } - + ColumnFamilyAccessResult evaluateAccess(ObserverContext ctx, String operation, Action action, final RegionCoprocessorEnvironment env, - final Map> familyMap, String commandStr) throws AccessDeniedException { + final Map> familyMap, String commandStr) throws AccessDeniedException { + if (LOG.isDebugEnabled()) { + LOG.debug("evaluateAccess: isColumnAuthOptimizationEnabled="+hbasePlugin.getPropertyIsColumnAuthOptimizationEnabled()); + } String access = _authUtils.getAccess(action); User user = getActiveUser(ctx); @@ -393,7 +414,7 @@ ColumnFamilyAccessResult evaluateAccess(ObserverContext ctx, String operation } else { LOG.debug("evaluateAccess: Families collection not null. Skipping table-level check, will do finer level check"); } - + boolean everythingIsAccessible = true; boolean somethingIsAccessible = false; /* @@ -409,6 +430,7 @@ ColumnFamilyAccessResult evaluateAccess(ObserverContext ctx, String operation Set familesAccessAllowed = new HashSet(); Set familesAccessDenied = new HashSet(); Set familesAccessIndeterminate = new HashSet(); + Set familiesFullyAuthorized = new HashSet<>(); for (Map.Entry> anEntry : familyMap.entrySet()) { String family = Bytes.toString(anEntry.getKey()); @@ -440,6 +462,8 @@ ColumnFamilyAccessResult evaluateAccess(ObserverContext ctx, String operation if (LOG.isDebugEnabled()) { LOG.debug("evaluateAccess: family level access for [" + family + "] is evaluated to " + isColumnFamilyAuthorized + ". Checking if [" + family + "] descendants have access."); } + // buildRequest again since resourceMatchingScope changed + // reset ResourceMatchingScope to SELF, ignoreDescendantDeny to true session.resourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) .ignoreDescendantDeny(false) .buildRequest() @@ -491,7 +515,44 @@ ColumnFamilyAccessResult evaluateAccess(ObserverContext ctx, String operation session.resourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF); session.ignoreDescendantDeny(true); } else { - LOG.debug("evaluateAccess: columns collection not empty. Skipping Family level check, will do finer level access check."); + boolean isColumnAuthShortCircuitingEnabled = hbasePlugin.getPropertyIsColumnAuthOptimizationEnabled(); + if(LOG.isDebugEnabled()) { + LOG.debug("evaluateAccess: columns collection not empty." + + " Skipping Family level check, will do finer level access check for columns."); + } + if (isColumnAuthShortCircuitingEnabled) { + session.column(null) + .buildRequest() + .authorize(); + + boolean isColumnFamilyAuthorized = session.isAuthorized(); + + //check if column family fully authorized i.e. no deny for columns + session.column(null) + .resourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) + .ignoreDescendantDeny(false) + .buildRequest() + .authorize(); + + boolean isColumnFamilyAndDescendantsAuthorized = session.isAuthorized() && isColumnFamilyAuthorized; + AuthzAuditEvent auditEvent = auditHandler.getAndDiscardMostRecentEvent(); + // reset ResourceMatchingScope to SELF, ignoreDescendantDeny to true + session.resourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF) + .ignoreDescendantDeny(true); + if (LOG.isDebugEnabled()) { + LOG.debug("evaluateAccess: isColumnAuthShortCircuitingEnabled=true, isColumnFamilyAndDescendantsAuthorized={}",isColumnFamilyAndDescendantsAuthorized); + } + if (isColumnFamilyAndDescendantsAuthorized) { + familiesFullyAuthorized.add(family); + if (auditEvent != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("evaluateAccess: isColumnAuthShortCircuitingEnabled=true, adding to familiesFullyAuthorized set"); + } + familyLevelAccessEvents.add(auditEvent); + } + continue; + } + } Set accessibleColumns = new HashSet(); // will be used in to populate our results cache for the filter Iterator columnIterator = new ColumnIterator(columns); while (columnIterator.hasNext()) { @@ -499,6 +560,7 @@ ColumnFamilyAccessResult evaluateAccess(ObserverContext ctx, String operation if (LOG.isDebugEnabled()) { LOG.debug("evaluateAccess: Processing column: " + column); } + //buildRequest required again since now column is being set session.column(column) .buildRequest() .authorize(); @@ -533,7 +595,7 @@ ColumnFamilyAccessResult evaluateAccess(ObserverContext ctx, String operation } } // Cache of auth results are encapsulated the in the filter. Not every caller of the function uses it - only preGet and preOpt will. - RangerAuthorizationFilter filter = new RangerAuthorizationFilter(session, familesAccessAllowed, familesAccessDenied, familesAccessIndeterminate, columnsAccessAllowed); + RangerAuthorizationFilter filter = new RangerAuthorizationFilter(session, familesAccessAllowed, familesAccessDenied, familesAccessIndeterminate, columnsAccessAllowed, familiesFullyAuthorized); result = new ColumnFamilyAccessResult(everythingIsAccessible, somethingIsAccessible, authorizedEvents, familyLevelAccessEvents, deniedEvent, denialReason, filter); if (LOG.isDebugEnabled()) { String message = String.format(messageTemplate, userName, operation, access, colFamiliesForDebugLoggingOnly, result.toString()); @@ -577,7 +639,7 @@ Filter authorizeAccess(ObserverContext ctx, String operation, Action action, } } } - + Filter combineFilters(Filter filter, Filter existingFilter) { Filter combinedFilter = filter; if (existingFilter != null) { @@ -611,7 +673,7 @@ void requirePermission(final ObserverContext ctx, final String operation, fin RangerPerfTracer.log(perf); } } - + /** * This could run s * @param operation @@ -630,7 +692,7 @@ void authorizeAccess(ObserverContext ctx, String operation, String otherInfor String message = String.format(format, "Entering", operation, otherInformation, access, table, columnFamily, column); LOG.debug(message); } - + final String format = "authorizeAccess: %s: Operation[%s], Info[%s], access[%s], table[%s], columnFamily[%s], column[%s], allowed[%s], reason[%s]"; if (canSkipAccessCheck(user, operation, access, table)) { if (LOG.isDebugEnabled()) { @@ -640,7 +702,7 @@ void authorizeAccess(ObserverContext ctx, String operation, String otherInfor return; } - + HbaseAuditHandler auditHandler = _factory.getAuditHandler(); AuthorizationSession session = new AuthorizationSession(hbasePlugin) .operation(operation) @@ -654,20 +716,20 @@ void authorizeAccess(ObserverContext ctx, String operation, String otherInfor .column(column) .buildRequest() .authorize(); - + if (LOG.isDebugEnabled()) { boolean allowed = session.isAuthorized(); String reason = session.getDenialReason(); String message = String.format(format, "Exiting", operation, otherInformation, access, table, columnFamily, column, allowed, reason); LOG.debug(message); } - + session.publishResults(); } - + boolean canSkipAccessCheck(User user, final String operation, String access, final String table) throws AccessDeniedException { - + boolean result = false; if (user == null) { String message = "Unexpeceted: User is null: access denied, not audited!"; @@ -679,10 +741,10 @@ boolean canSkipAccessCheck(User user, final String operation, String access, fin } else { LOG.debug("Can't skip access checks"); } - + return result; } - + boolean canSkipAccessCheck(User user, final String operation, String access, final RegionCoprocessorEnvironment regionServerEnv) throws AccessDeniedException { // read access to metadata tables is always allowed and isn't audited. @@ -708,7 +770,7 @@ boolean canSkipAccessCheck(User user, final String operation, String access, fin } return false; } - + boolean isAccessForMetadataRead(String access, String table) { if (_authUtils.isReadAccess(access) && isSpecialTable(table)) { LOG.debug("isAccessForMetadataRead: Metadata tables read: access allowed!"); @@ -731,7 +793,7 @@ protected void requirePermission(ObserverContext ctx, String request, byte[] authorizeAccess(ctx, request, null, action, table, null, null); } - + protected void requirePermission(ObserverContext ctx, String request, byte[] aTableName, byte[] aColumnFamily, byte[] aQualifier, Permission.Action action) throws AccessDeniedException { String table = Bytes.toString(aTableName); @@ -740,7 +802,7 @@ protected void requirePermission(ObserverContext ctx, String request, byte[] authorizeAccess(ctx, request, null, action, table, columnFamily, column); } - + protected void requirePermission(ObserverContext ctx, String request, Permission.Action perm, RegionCoprocessorEnvironment env, Collection families) throws IOException { HashMap> familyMap = new HashMap>(); @@ -929,7 +991,7 @@ public void preFlush(ObserverContext e, FlushLifeC @Override public Result preIncrement(ObserverContext c, Increment increment) throws IOException { requirePermission(c, "increment", TablePermission.Action.WRITE, c.getEnvironment(), increment.getFamilyCellMap().keySet()); - + return null; } @@ -1123,7 +1185,7 @@ public void start(CoprocessorEnvironment env) throws IOException { if(plugin == null) { synchronized(RangerAuthorizationCoprocessor.class) { plugin = hbasePlugin; - + if(plugin == null) { plugin = new RangerHBasePlugin(appType); @@ -1135,7 +1197,7 @@ public void start(CoprocessorEnvironment env) throws IOException { } } } - + if (LOG.isDebugEnabled()) { LOG.debug("Start of Coprocessor: [" + coprocessorType + "]"); } @@ -1145,7 +1207,7 @@ public void start(CoprocessorEnvironment env) throws IOException { public void prePut(ObserverContext c, Put put, WALEdit edit, Durability durability) throws IOException { requirePermission(c, "put", TablePermission.Action.WRITE, c.getEnvironment(), put.getFamilyCellMap()); } - + @Override public void preGetOp(final ObserverContext rEnv, final Get get, final List result) throws IOException { if (LOG.isDebugEnabled()) { @@ -1216,7 +1278,7 @@ public void postGetTableDescriptors(ObserverContext tableDescriptors = admin.listTableDescriptors(); + for (TableDescriptor desc : tableDescriptors) { LOG.info("Found table:[" + desc.getTableName().getNameAsString() + "]"); } - Assert.assertEquals(3, tableDescriptors.length); + Assert.assertEquals(6, tableDescriptors.size()); conn.close(); } - + // This should fail as the "IT" group only has read privileges, not admin privileges, on the table "temp" @Test public void testReadTablesAsGroupIT() throws Exception { @@ -200,7 +232,7 @@ public void testReadTablesAsGroupIT() throws Exception { conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + String user = "IT"; UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"IT"}); @@ -208,40 +240,40 @@ public void testReadTablesAsGroupIT() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); - - HTableDescriptor[] tableDescriptors = admin.listTables(); - for (HTableDescriptor desc : tableDescriptors) { + + List tableDescriptors = admin.listTableDescriptors(); + for (TableDescriptor desc : tableDescriptors) { LOG.info("Found table:[" + desc.getTableName().getNameAsString() + "]"); } - Assert.assertEquals(0, tableDescriptors.length); - + Assert.assertEquals(0, tableDescriptors.size()); + conn.close(); return null; } }); } - + @Test public void testCreateAndDropTables() throws Exception { final Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); // Create a new table as process owner - HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("temp2")); + TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("temp2")); // Adding column families to table descriptor - tableDescriptor.addFamily(new HColumnDescriptor("colfam1")); - tableDescriptor.addFamily(new HColumnDescriptor("colfam2")); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam1".getBytes()).build()); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam2".getBytes()).build()); - admin.createTable(tableDescriptor); + admin.createTable(tableDescriptor.build()); conn.close(); - + // Try to disable + delete the table as the "IT" group String user = "IT"; @@ -250,7 +282,7 @@ public void testCreateAndDropTables() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); - + try { admin.disableTable(TableName.valueOf("temp2")); admin.deleteTable(TableName.valueOf("temp2")); @@ -258,40 +290,40 @@ public Void run() throws Exception { } catch (IOException ex) { // expected } - + conn.close(); return null; } }); - + // Now disable and delete as process owner conn = ConnectionFactory.createConnection(conf); admin = conn.getAdmin(); admin.disableTable(TableName.valueOf("temp2")); admin.deleteTable(TableName.valueOf("temp2")); - + conn.close(); } - + @Test public void testReadRowAsProcessOwner() throws Exception { final Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Read a row Get get = new Get(Bytes.toBytes("row1")); Result result = table.get(get); byte[] valResult = result.getValue(Bytes.toBytes("colfam1"), Bytes.toBytes("col1")); Assert.assertTrue(Arrays.equals(valResult, Bytes.toBytes("val1"))); - + conn.close(); } - + @Test public void testReadRowAsGroupIT() throws Exception { final Configuration conf = HBaseConfiguration.create(); @@ -306,7 +338,7 @@ public void testReadRowAsGroupIT() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Read a row Get get = new Get(Bytes.toBytes("row1")); Result result = table.get(get); @@ -318,7 +350,7 @@ public Void run() throws Exception { } }); } - + // This should fail as "public" doesn't have the right to read the table @Test public void testReadRowAsGroupPublic() throws Exception { @@ -334,7 +366,7 @@ public void testReadRowAsGroupPublic() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Read a row try { Get get = new Get(Bytes.toBytes("row1")); @@ -350,14 +382,14 @@ public Void run() throws Exception { } }); } - + @Test public void testReadRowFromColFam2AsProcessOwner() throws Exception { final Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); @@ -369,7 +401,7 @@ public void testReadRowFromColFam2AsProcessOwner() throws Exception { conn.close(); } - + @Test public void testReadRowFromColFam2AsGroupIT() throws Exception { final Configuration conf = HBaseConfiguration.create(); @@ -384,7 +416,7 @@ public void testReadRowFromColFam2AsGroupIT() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Read a row Get get = new Get(Bytes.toBytes("row1")); Result result = table.get(get); @@ -396,25 +428,25 @@ public Void run() throws Exception { } }); } - + @Test public void testWriteRowAsProcessOwner() throws Exception { final Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Add a new row Put put = new Put(Bytes.toBytes("row2")); put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); table.put(put); - + conn.close(); } - + @Test public void testWriteRowAsGroupIT() throws Exception { final Configuration conf = HBaseConfiguration.create(); @@ -429,18 +461,18 @@ public void testWriteRowAsGroupIT() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Add a new row Put put = new Put(Bytes.toBytes("row3")); put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); table.put(put); - + conn.close(); return null; } }); } - + @Test public void testWriteRowAsGroupPublic() throws Exception { final Configuration conf = HBaseConfiguration.create(); @@ -455,7 +487,7 @@ public void testWriteRowAsGroupPublic() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Add a new row try { Put put = new Put(Bytes.toBytes("row3")); @@ -465,13 +497,13 @@ public Void run() throws Exception { } catch (IOException ex) { // expected } - + conn.close(); return null; } }); } - + @Test public void testWriteRowInColFam2AsGroupIT() throws Exception { final Configuration conf = HBaseConfiguration.create(); @@ -486,7 +518,7 @@ public void testWriteRowInColFam2AsGroupIT() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Add a new row try { Put put = new Put(Bytes.toBytes("row3")); @@ -496,46 +528,46 @@ public Void run() throws Exception { } catch (IOException ex) { // expected } - + conn.close(); return null; } }); } - + @Test public void testReadRowInAnotherTable() throws Exception { final Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); // Create a new table as process owner - HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("temp4")); + TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("temp4")); // Adding column families to table descriptor - tableDescriptor.addFamily(new HColumnDescriptor("colfam1")); - tableDescriptor.addFamily(new HColumnDescriptor("colfam2")); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam1".getBytes()).build()); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam2".getBytes()).build()); - admin.createTable(tableDescriptor); + admin.createTable(tableDescriptor.build()); // Write a value Put put = new Put(Bytes.toBytes("row1")); put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val1")); Table table = conn.getTable(TableName.valueOf("temp4")); table.put(put); - + // Read a row Get get = new Get(Bytes.toBytes("row1")); Result result = table.get(get); byte[] valResult = result.getValue(Bytes.toBytes("colfam2"), Bytes.toBytes("col1")); Assert.assertNull(valResult); - + conn.close(); - + // Now try to read the row as group "IT" - it should fail as "IT" can only read from table "temp" String user = "IT"; @@ -544,7 +576,7 @@ public void testReadRowInAnotherTable() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp4")); - + // Read a row try { Get get = new Get(Bytes.toBytes("row1")); @@ -559,48 +591,48 @@ public Void run() throws Exception { return null; } }); - + // Now disable and delete as process owner conn = ConnectionFactory.createConnection(conf); admin = conn.getAdmin(); admin.disableTable(TableName.valueOf("temp4")); admin.deleteTable(TableName.valueOf("temp4")); - + conn.close(); } - + @Test public void testDeleteRowAsProcessOwner() throws Exception { final Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Add a new row Put put = new Put(Bytes.toBytes("row4")); put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); table.put(put); - + // Delete the new row Delete delete = new Delete(Bytes.toBytes("row4")); table.delete(delete); - + conn.close(); } - + @Test public void testDeleteRowAsGroupIT() throws Exception { final Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - + Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + // Add a new row (as process owner) Put put = new Put(Bytes.toBytes("row5")); put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); @@ -613,7 +645,7 @@ public void testDeleteRowAsGroupIT() throws Exception { public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TableName.valueOf("temp")); - + try { // Delete the new row Delete delete = new Delete(Bytes.toBytes("row5")); @@ -622,16 +654,16 @@ public Void run() throws Exception { } catch (IOException ex) { // expected } - + conn.close(); return null; } }); - + // Delete the new row (as process owner) Delete delete = new Delete(Bytes.toBytes("row5")); table.delete(delete); - + conn.close(); } @@ -666,7 +698,8 @@ public Void run() throws Exception { admin.snapshot("test_snapshot", tableName); // Clone snapshot - HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("test_namespace", "temp_cloned")); + TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("test_namespace", "temp_cloned")).build(); + TableName newTableName = tableDescriptor.getTableName(); admin.cloneSnapshot("test_snapshot", newTableName); admin.disableTable(newTableName); @@ -746,11 +779,11 @@ public void testTagBasedTablePolicy() throws Exception { conf.set("hbase.zookeeper.property.clientPort", "" + port); conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - final HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("temp3")); + final TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("temp3")); // Adding column families to table descriptor - tableDescriptor.addFamily(new HColumnDescriptor("colfam1")); - tableDescriptor.addFamily(new HColumnDescriptor("colfam2")); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam1".getBytes()).build()); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam2".getBytes()).build()); // Try to create a "temp3" table as the "IT" group - this should fail String user = "IT"; @@ -763,7 +796,7 @@ public Void run() throws Exception { Admin admin = conn.getAdmin(); try { - admin.createTable(tableDescriptor); + admin.createTable(tableDescriptor.build()); Assert.fail("Failure expected on an unauthorized user"); } catch (IOException ex) { // expected @@ -781,7 +814,7 @@ public Void run() throws Exception { Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); - admin.createTable(tableDescriptor); + admin.createTable(tableDescriptor.build()); conn.close(); return null; @@ -806,16 +839,16 @@ public void testTagBasedColumnFamilyPolicy() throws Exception { conf.set("zookeeper.znode.parent", "/hbase-unsecure"); // Create a new table as process owner - final HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("temp3")); + final TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("temp3")); // Adding column families to table descriptor - tableDescriptor.addFamily(new HColumnDescriptor("colfam1")); - tableDescriptor.addFamily(new HColumnDescriptor("colfam2")); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam1".getBytes()).build()); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam2".getBytes()).build()); Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); - admin.createTable(tableDescriptor); + admin.createTable(tableDescriptor.build()); // Add a new row Put put = new Put(Bytes.toBytes("row1")); @@ -891,16 +924,16 @@ public void testTagBasedColumnPolicy() throws Exception { conf.set("zookeeper.znode.parent", "/hbase-unsecure"); // Create a new table as process owner - final HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("temp3")); + final TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("temp3")); // Adding column families to table descriptor - tableDescriptor.addFamily(new HColumnDescriptor("colfam1")); - tableDescriptor.addFamily(new HColumnDescriptor("colfam2")); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam1".getBytes()).build()); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam2".getBytes()).build()); Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); - admin.createTable(tableDescriptor); + admin.createTable(tableDescriptor.build()); // Add a new row Put put = new Put(Bytes.toBytes("row1")); @@ -975,64 +1008,431 @@ public Void run() throws Exception { conn.close(); } - @Test - public void testGetUserPermission() throws Throwable { - final Configuration conf = HBaseConfiguration.create(); - conf.set("hbase.zookeeper.quorum", "localhost"); - conf.set("hbase.zookeeper.property.clientPort", "" + port); - conf.set("zookeeper.znode.parent", "/hbase-unsecure"); - String user = "IT"; - UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] { "IT" }); - ugi.doAs(new PrivilegedExceptionAction() { - public Void run() throws Exception { - try (Connection conn = ConnectionFactory.createConnection(conf)) { - AccessControlClient.getUserPermissions(conn, "temp"); - Assert.fail(); - } catch (Throwable e) { - // expected - } - return null; - } - - }); - - user = "QA"; - ugi = UserGroupInformation.createUserForTesting(user, new String[] { "QA" }); - ugi.doAs(new PrivilegedExceptionAction() { - public Void run() throws Exception { - List userPermissions; - try (Connection conn = ConnectionFactory.createConnection(conf)) { - userPermissions = AccessControlClient.getUserPermissions(conn, "@test_namespace"); - } catch (Throwable e) { - throw new Exception(e); - } - boolean found = false; - for (UserPermission namespacePermission : userPermissions) { - if (namespacePermission.getPermission() instanceof NamespacePermission) { - found = StringUtils.equals(namespacePermission.getUser(), "@QA"); - if (found) { - break; - } - } - } - Assert.assertTrue("QA is not found", found); - return null; - } - }); - - List userPermissions; - try (Connection conn = ConnectionFactory.createConnection(conf)) { - userPermissions = AccessControlClient.getUserPermissions(conn, "temp5"); - } catch (Throwable e) { - throw new Exception(e); - } - - UserPermission userPermission = new UserPermission("@IT", - Permission.newBuilder(TableName.valueOf("temp5")).withActions(Permission.Action.READ, Permission.Action.WRITE, Permission.Action.EXEC).build()); - - Assert.assertTrue("@IT permission should be there", userPermissions.contains(userPermission)); - - } + @Test + public void testGetUserPermission() throws Throwable { + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + String user = "IT"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] { "IT" }); + if(!utility.getHBaseCluster().isDistributedCluster()) { + RangerAuthorizationCoprocessor authorizationCoprocessor = + utility.getHBaseCluster().getMaster().getMasterCoprocessorHost(). + findCoprocessor(RangerAuthorizationCoprocessor.class); + RpcController rpcController = new RpcController() { + @Override + public void reset() { + + } + + @Override + public boolean failed() { + return false; + } + + @Override + public String errorText() { + return null; + } + + @Override + public void startCancel() { + + } + + @Override + public void setFailed(String reason) { + + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public void notifyOnCancel(RpcCallback callback) { + + } + }; + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + AccessControlProtos.GetUserPermissionsRequest requestTablePerms = getTableUserPermissions("temp"); + authorizationCoprocessor.getUserPermissions(rpcController, requestTablePerms, + new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse message) { + if (message != null) { + for (AccessControlProtos.UserPermission perm : message + .getUserPermissionList()) { + AccessControlUtil.toUserPermission(perm); + Assert.fail(); + } + } + } + }); + return null; + } + + }); + + user = "QA"; + ugi = UserGroupInformation.createUserForTesting(user, new String[] { "QA" }); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + final List userPermissions = new ArrayList(); + AccessControlProtos.GetUserPermissionsRequest requestTablePerms = getNamespaceUserPermissions("test_namespace"); + getUserPermissions(userPermissions, requestTablePerms, authorizationCoprocessor, rpcController); + boolean found = false; + for (UserPermission namespacePermission : userPermissions) { + if (namespacePermission.getPermission() instanceof NamespacePermission ) { + found = Bytes.equals(namespacePermission.getUser().getBytes(), Bytes.toBytes("@QA")); + if (found) { + break; + } + } + } + Assert.assertTrue("QA is not found", found); + return null; + } + }); + + final List userPermissions = new ArrayList<>(); + AccessControlProtos.GetUserPermissionsRequest requestTablePerms = getTableUserPermissions("temp5"); + getUserPermissions(userPermissions, requestTablePerms, authorizationCoprocessor, rpcController); + Permission p = Permission.newBuilder(TableName.valueOf("temp5")). + withActions(Permission.Action.READ, Permission.Action.WRITE, Permission.Action.EXEC).build(); + UserPermission userPermission = new UserPermission("@IT", p); + Assert.assertTrue("@IT permission should be there", userPermissions.contains(userPermission)); + } + } + @Test + public void testWriteRowAsGroupIT2() throws Exception { + //check access to table temp6 to test the policy TempPolicyForOptimizedColAuth with non * column + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + String user = "IT"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp6")); + // Add a new row + Put put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + table.put(put); + conn.close(); + return null; + } + }); + } + @Test + public void testWriteRowAsGroupIT2Optimized() throws Exception { + // No behavior change from testWriteRowAsGroupIT2() + enableColumnAuthOptimization(true); + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + String user = "IT"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp6")); + // Add a new row + Put put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + table.put(put); + conn.close(); + return null; + } + }); + enableColumnAuthOptimization(false); + } + @Test + public void testWriteRowDeniedAsGroupIT2() throws Exception { + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + String user = "IT"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp7")); + try { + // Add a new row + Put put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + table.put(put); + Assert.fail("Failure expected on an unauthorized user"); + } + catch (IOException ex) { + // expected + } + conn.close(); + return null; + } + }); + } + @Test + public void testWriteRowDeniedAsGroupIT2Optimized() throws Exception { + // no behavior change from testWriteRowDeniedAsGroupIT2 + enableColumnAuthOptimization(true); + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + String user = "IT"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp7")); + try { + // Add a new row + Put put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + table.put(put); + Assert.fail("Failure expected on an unauthorized user"); + } + catch (IOException ex) { + // expected + } + conn.close(); + return null; + } + }); + enableColumnAuthOptimization(false); + } + @Test + public void testScanTableAsGroupIT() throws Exception { + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + String user = "IT"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp8")); + try { + Scan scan = new Scan(); + ResultScanner scanner = table.getScanner(scan); + int numRowsInResult = 0; + for (Result result = scanner.next(); result != null; result = scanner.next()){ + System.out.println("Found row : " + result); + numRowsInResult += 1; + } + //while there are 2 rows in this table, one of the columns is explicitly denied so only one column should be in the result + Assert.assertEquals(1,numRowsInResult); + } + catch (IOException ex) { + // expected + } + conn.close(); + return null; + } + }); + + user = "IT2"; + ugi = UserGroupInformation.createUserForTesting(user, new String[] {"IT2"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp8")); + try { + Scan scan = new Scan(); + ResultScanner scanner = table.getScanner(scan); + int numRowsInResult = 0; + for (Result result = scanner.next(); result != null; result = scanner.next()){ + System.out.println("Found row : " + result); + numRowsInResult += 1; + } + //there are 2 rows in this table, group IT2 does not have any denied columns + Assert.assertEquals(2,numRowsInResult); + } + catch (IOException ex) { + // expected + } + conn.close(); + return null; + } + }); + } + + + @Test + public void testWriteRowAsGroupPublicOptimized() throws Exception { + enableColumnAuthOptimization(true); // enable optimization + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + String user = "public"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"public"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp")); + // Add a new row + try { + Put put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + table.put(put); + Assert.fail("Failure expected on an unauthorized user"); + } catch (IOException ex) { + // expected + } + conn.close(); + return null; + } + }); + enableColumnAuthOptimization(false); // disable optimization after test case complete + } + @Test + public void testTagBasedColumnPolicyOptimized() throws Exception { + // There should not be any behavior change from testTagBasedColumnPolicy() even after + // column optimization enabled as tags will exist for the accessed resource and should + // fall back to default behavior + enableColumnAuthOptimization(true); + final Configuration conf = HBaseConfiguration.create(); + conf.set("hbase.zookeeper.quorum", "localhost"); + conf.set("hbase.zookeeper.property.clientPort", "" + port); + conf.set("zookeeper.znode.parent", "/hbase-unsecure"); + + // Create a new table as process owner + final TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("temp3")); + + // Adding column families to table descriptor + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam1".getBytes()).build()); + tableDescriptor.addColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder("colfam2".getBytes()).build()); + + Connection conn = ConnectionFactory.createConnection(conf); + Admin admin = conn.getAdmin(); + + admin.createTable(tableDescriptor.build()); + + // Add a new row + Put put = new Put(Bytes.toBytes("row1")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val1")); + Table table = conn.getTable(TableName.valueOf("temp3")); + table.put(put); + + put = new Put(Bytes.toBytes("row1")); + put.addColumn(Bytes.toBytes("colfam2"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + table.put(put); + + conn.close(); + + String user = "dev"; + UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {"dev"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp3")); + + // Try to write something to the "col1" column of the "colfam1" of the "temp3" table as the "dev" group + // - this should work + Put put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + table.put(put); + + // Try to write something to the "col2" column of the "colfam1" of the "temp3" table as the "dev" group + // - this should fail + put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col2"), Bytes.toBytes("val2")); + try { + table.put(put); + Assert.fail("Failure expected on an unauthorized user"); + } catch (IOException ex) { + // expected + } + + conn.close(); + return null; + } + }); + + ugi = UserGroupInformation.createUserForTesting("IT", new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + Connection conn = ConnectionFactory.createConnection(conf); + Table table = conn.getTable(TableName.valueOf("temp3")); + + // Try to write something to the "col1" column of the "colfam1" of the "temp3" table as the "IT" group + // - this should fail + Put put = new Put(Bytes.toBytes("row3")); + put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val2")); + try { + table.put(put); + Assert.fail("Failure expected on an unauthorized user"); + } catch (IOException ex) { + // expected + } + conn.close(); + return null; + } + }); + + // Drop the table + conn = ConnectionFactory.createConnection(conf); + admin = conn.getAdmin(); + + admin.disableTable(TableName.valueOf("temp3")); + admin.deleteTable(TableName.valueOf("temp3")); + + conn.close(); + enableColumnAuthOptimization(false); + } + + private static void enableColumnAuthOptimization(boolean enable){ + RangerAuthorizationCoprocessor authorizationCoprocessor = + utility.getHBaseCluster().getMaster().getMasterCoprocessorHost(). + findCoprocessor(RangerAuthorizationCoprocessor.class); + try { + authorizationCoprocessor.setColumnAuthOptimizationEnabled(enable); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private AccessControlProtos.GetUserPermissionsRequest getNamespaceUserPermissions(String namespace) { + AccessControlProtos.GetUserPermissionsRequest.Builder builderTablePerms = AccessControlProtos.GetUserPermissionsRequest + .newBuilder(); + builderTablePerms.setNamespaceName(ByteString.copyFromUtf8(namespace)); + builderTablePerms.setType(AccessControlProtos.Permission.Type.Namespace); + return builderTablePerms.build(); + } + + private AccessControlProtos.GetUserPermissionsRequest getTableUserPermissions(String tableName) { + AccessControlProtos.GetUserPermissionsRequest.Builder builderTablePerms = AccessControlProtos.GetUserPermissionsRequest + .newBuilder(); + builderTablePerms.setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf(tableName))); + builderTablePerms.setType(AccessControlProtos.Permission.Type.Table); + return builderTablePerms.build(); + } + + private void getUserPermissions(List userPermissions, AccessControlProtos.GetUserPermissionsRequest requestTablePerms, RangerAuthorizationCoprocessor authorizationCoprocessor, RpcController rpcController) { + authorizationCoprocessor.getUserPermissions(rpcController, requestTablePerms, + new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse message) { + if (message != null) { + for (AccessControlProtos.UserPermission perm : message + .getUserPermissionList()) { + userPermissions.add(AccessControlUtil.toUserPermission(perm)); + } + } + } + }); + } private static int getFreePort() throws IOException { ServerSocket serverSocket = new ServerSocket(0); @@ -1040,4 +1440,5 @@ private static int getFreePort() throws IOException { serverSocket.close(); return port; } -} + +} \ No newline at end of file diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java index 6b97c30645..fffc7943db 100644 --- a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -54,7 +55,7 @@ public void testFilterKeyValueCell_happyPath() throws IOException { // auth session AuthorizationSession session = createSessionMock(); - RangerAuthorizationFilter filter = new RangerAuthorizationFilter(session, familiesAccessAllowed, familiesAccessDenied, familiesAccessIndeterminate, columnsAccessAllowed); + RangerAuthorizationFilter filter = new RangerAuthorizationFilter(session, familiesAccessAllowed, familiesAccessDenied, familiesAccessIndeterminate, columnsAccessAllowed, new HashSet<>()); // evaluate access for various types of cases Cell aCell = mock(Cell.class); diff --git a/hbase-agent/src/test/resources/hbase-policies.json b/hbase-agent/src/test/resources/hbase-policies.json index 61960c0af2..8ee73b1e09 100644 --- a/hbase-agent/src/test/resources/hbase-policies.json +++ b/hbase-agent/src/test/resources/hbase-policies.json @@ -245,6 +245,101 @@ "id": 38, "isEnabled": true, "version": 1 + }, + { + "service": "cl1_hbase", "name": "TempPolicyForOptimizedColAuth", "policyType": 0, "description": "", "isAuditEnabled": true, + "resources": { + "column-family": {"values": ["colfam1"], "isExcludes": false, "isRecursive": false }, + "column": {"values": ["col1"], "isExcludes": false, "isRecursive": false}, + "table": {"values": ["temp6"], "isExcludes": false, "isRecursive": false} + }, + "policyItems": [ + { + "accesses": [ + {"type": "read", "isAllowed": true}, + {"type": "write", "isAllowed": true}, + {"type": "execute", "isAllowed": true} + ], + "users": [], "groups": ["IT"], "conditions": [], "delegateAdmin": false + } + ], + "id": 101, "isEnabled": true, "version": 1 + }, + { + "service": "cl1_hbase", "name": "AllowColFamWhenColDeniedForOptimized", "policyType": 0, "description": "", "isAuditEnabled": true, + "resources": { + "column-family": {"values": ["colfam1"], "isExcludes": false, "isRecursive": false}, + "column": {"values": ["*"], "isExcludes": false, "isRecursive": false}, + "table": {"values": ["temp7"], "isExcludes": false, "isRecursive": false} + }, + "policyItems": [ + { + "accesses": [ + {"type": "read", "isAllowed": true}, + {"type": "write", "isAllowed": true}, + {"type": "execute", "isAllowed": true} + ], + "users": [], "groups": ["IT"], "conditions": [], "delegateAdmin": false + } + ], + "id": 102, "isEnabled": true, "version": 1 + }, + { + "service": "cl1_hbase", "name": "DenyColWhenColFamAllowedForOptimizedCheck", "policyType": 0, "description": "", "isAuditEnabled": true, + "resources": { + "column-family": {"values": ["colfam1"], "isExcludes": false, "isRecursive": false}, + "column": {"values": ["col1"], "isExcludes": false, "isRecursive": false}, + "table": {"values": ["temp7"], "isExcludes": false, "isRecursive": false} + }, + "denyPolicyItems": [ + { + "accesses": [ + {"type": "read", "isAllowed": true}, + {"type": "write", "isAllowed": true}, + {"type": "execute", "isAllowed": true} + ], + "users": [], "groups": ["IT"], "conditions": [], "delegateAdmin": false + } + ], + "id": 103, "isEnabled": true, "version": 1 + }, + { + "service": "cl1_hbase", "name": "PolicyForAllowColumnFamilyForScan", "policyType": 0, "description": "", "isAuditEnabled": true, + "resources": { + "column-family": {"values": ["colfam1"], "isExcludes": false, "isRecursive": false}, + "column": {"values": ["*"], "isExcludes": false, "isRecursive": false}, + "table": {"values": ["temp8"], "isExcludes": false, "isRecursive": false} + }, + "policyItems": [ + { + "accesses": [ + {"type": "read", "isAllowed": true}, + {"type": "write", "isAllowed": true}, + {"type": "execute", "isAllowed": true} + ], + "users": [], "groups": ["IT","IT2"], "conditions": [], "delegateAdmin": false + } + ], + "id": 104, "isEnabled": true, "version": 1 + }, + { + "service": "cl1_hbase", "name": "PolicyForDenyColumnForScan", "policyType": 0, "description": "", "isAuditEnabled": true, + "resources": { + "column-family": {"values": ["colfam1"], "isExcludes": false, "isRecursive": false}, + "column": {"values": ["col1"], "isExcludes": false, "isRecursive": false}, + "table": {"values": ["temp8"], "isExcludes": false, "isRecursive": false} + }, + "denyPolicyItems": [ + { + "accesses": [ + {"type": "read", "isAllowed": true}, + {"type": "write", "isAllowed": true}, + {"type": "execute", "isAllowed": true} + ], + "users": [], "groups": ["IT"], "conditions": [], "delegateAdmin": false + } + ], + "id": 105, "isEnabled": true, "version": 1 } ], "serviceDef": { @@ -1317,4 +1412,4 @@ }, "auditMode": "audit-default" } -} +} \ No newline at end of file