This is an automated email from the ASF dual-hosted git repository. ctubbsii pushed a commit to branch 1.9 in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/1.9 by this push: new 375cfbc Expanded InputConfigurator permissions checks to include Namespace.READ (#1371) 375cfbc is described below commit 375cfbc898fe49b80f8ae6fc26167ded332e3709 Author: Richard W. Eggert II <richard.egg...@gmail.com> AuthorDate: Fri Oct 4 19:03:36 2019 -0400 Expanded InputConfigurator permissions checks to include Namespace.READ (#1371) * created tests to cover #1370 * expanded checks in InputConfigurator.validatePermissions to include Namespace.READ and resolve #1370 * auto-adjusted formatting * removed unnecessary assertions from testGetSplitsWithNamespaceReadPermission --- .../mapreduce/lib/impl/InputConfigurator.java | 21 +++++- .../test/mapreduce/AccumuloInputFormatIT.java | 74 ++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/apache/accumulo/core/client/mapreduce/lib/impl/InputConfigurator.java b/core/src/main/java/org/apache/accumulo/core/client/mapreduce/lib/impl/InputConfigurator.java index c978060..f5efdf7 100644 --- a/core/src/main/java/org/apache/accumulo/core/client/mapreduce/lib/impl/InputConfigurator.java +++ b/core/src/main/java/org/apache/accumulo/core/client/mapreduce/lib/impl/InputConfigurator.java @@ -66,6 +66,7 @@ import org.apache.accumulo.core.metadata.MetadataTable; import org.apache.accumulo.core.metadata.schema.MetadataSchema; import org.apache.accumulo.core.sample.impl.SamplerConfigurationImpl; import org.apache.accumulo.core.security.Authorizations; +import org.apache.accumulo.core.security.NamespacePermission; import org.apache.accumulo.core.security.TablePermission; import org.apache.accumulo.core.util.Base64; import org.apache.accumulo.core.util.DeprecationUtil; @@ -778,6 +779,15 @@ public class InputConfigurator extends ConfiguratorBase { return getInstance(implementingClass, conf); } + private static String extractNamespace(final String tableName) { + final int delimiterPos = tableName.indexOf('.'); + if (delimiterPos < 1) { + return ""; // default namespace + } else { + return tableName.substring(0, delimiterPos); + } + } + /** * Validates that the user has permissions on the requested tables * @@ -796,10 +806,17 @@ public class InputConfigurator extends ConfiguratorBase { if (getInputTableConfigs(implementingClass, conf).size() == 0) throw new IOException("No table set."); + final String principal = getPrincipal(implementingClass, conf); for (Map.Entry<String,InputTableConfig> tableConfig : inputTableConfigs.entrySet()) { - if (!conn.securityOperations().hasTablePermission(getPrincipal(implementingClass, conf), - tableConfig.getKey(), TablePermission.READ)) + final String tableName = tableConfig.getKey(); + final String namespace = extractNamespace(tableName); + final boolean hasTableRead = conn.securityOperations().hasTablePermission(principal, + tableName, TablePermission.READ); + final boolean hasNamespaceRead = conn.securityOperations().hasNamespacePermission(principal, + namespace, NamespacePermission.READ); + if (!hasTableRead && !hasNamespaceRead) { throw new IOException("Unable to access table"); + } } for (Map.Entry<String,InputTableConfig> tableConfigEntry : inputTableConfigs.entrySet()) { InputTableConfig tableConfig = tableConfigEntry.getValue(); diff --git a/test/src/main/java/org/apache/accumulo/test/mapreduce/AccumuloInputFormatIT.java b/test/src/main/java/org/apache/accumulo/test/mapreduce/AccumuloInputFormatIT.java index e9f1bd8..afce3ee 100644 --- a/test/src/main/java/org/apache/accumulo/test/mapreduce/AccumuloInputFormatIT.java +++ b/test/src/main/java/org/apache/accumulo/test/mapreduce/AccumuloInputFormatIT.java @@ -55,6 +55,7 @@ import org.apache.accumulo.core.data.Mutation; import org.apache.accumulo.core.data.Range; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.security.Authorizations; +import org.apache.accumulo.core.security.TablePermission; import org.apache.accumulo.core.util.Pair; import org.apache.accumulo.harness.AccumuloClusterHarness; import org.apache.accumulo.minicluster.impl.MiniAccumuloConfigImpl; @@ -463,6 +464,79 @@ public class AccumuloInputFormatIT extends AccumuloClusterHarness { assertEquals(level, risplit.getLogLevel()); } + @Test(expected = IOException.class) + public void testGetSplitsNoReadPermission() throws Exception { + Job job = Job.getInstance(); + + String table = getUniqueNames(1)[0]; + Authorizations auths = new Authorizations("foo"); + Collection<Pair<Text,Text>> fetchColumns = + Collections.singleton(new Pair<>(new Text("foo"), new Text("bar"))); + boolean isolated = true, localIters = true; + Level level = Level.WARN; + + Connector connector = getConnector(); + connector.tableOperations().create(table); + connector.securityOperations().revokeTablePermission(connector.whoami(), table, + TablePermission.READ); + + AccumuloInputFormat.setZooKeeperInstance(job, cluster.getClientConfig()); + AccumuloInputFormat.setConnectorInfo(job, getAdminPrincipal(), getAdminToken()); + + AccumuloInputFormat.setInputTableName(job, table); + AccumuloInputFormat.setScanAuthorizations(job, auths); + AccumuloInputFormat.setScanIsolation(job, isolated); + AccumuloInputFormat.setLocalIterators(job, localIters); + AccumuloInputFormat.fetchColumns(job, fetchColumns); + AccumuloInputFormat.setLogLevel(job, level); + + AccumuloInputFormat aif = new AccumuloInputFormat(); + + aif.getSplits(job); + } + + /* + * This tests the case where we do not have Table.READ permission, but we do have Namespace.READ. + * See issue #1370. + */ + @Test + public void testGetSplitsWithNamespaceReadPermission() throws Exception { + Job job = Job.getInstance(); + + final String[] namespaceAndTable = getUniqueNames(2); + final String namespace = namespaceAndTable[0]; + final String tableSimpleName = namespaceAndTable[1]; + final String table = namespace + "." + tableSimpleName; + Authorizations auths = new Authorizations("foo"); + Collection<Pair<Text,Text>> fetchColumns = + Collections.singleton(new Pair<>(new Text("foo"), new Text("bar"))); + final boolean isolated = true; + final boolean localIters = true; + Level level = Level.WARN; + + Connector connector = getConnector(); + connector.namespaceOperations().create(namespace); // creating namespace implies Namespace.READ + connector.tableOperations().create(table); + connector.securityOperations().revokeTablePermission(connector.whoami(), table, + TablePermission.READ); + + AccumuloInputFormat.setZooKeeperInstance(job, cluster.getClientConfig()); + AccumuloInputFormat.setConnectorInfo(job, getAdminPrincipal(), getAdminToken()); + + AccumuloInputFormat.setInputTableName(job, table); + AccumuloInputFormat.setScanAuthorizations(job, auths); + AccumuloInputFormat.setScanIsolation(job, isolated); + AccumuloInputFormat.setLocalIterators(job, localIters); + AccumuloInputFormat.fetchColumns(job, fetchColumns); + AccumuloInputFormat.setLogLevel(job, level); + + AccumuloInputFormat aif = new AccumuloInputFormat(); + + List<InputSplit> splits = aif.getSplits(job); + + assertEquals(1, splits.size()); + } + @Test public void testPartialInputSplitDelegationToConfiguration() throws Exception { String table = getUniqueNames(1)[0];