This is an automated email from the ASF dual-hosted git repository. ctubbsii pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/main by this push: new 58a559f Validate table/namespace names in public API (#1971) 58a559f is described below commit 58a559fa6ac7c5137b5cd1418b0de457f7cad2a9 Author: slackwinner <50567198+slackwin...@users.noreply.github.com> AuthorDate: Mon Jun 7 17:02:05 2021 -0400 Validate table/namespace names in public API (#1971) Throw IllegalArgumentException in Public API consistently after validating table and namespace names In response to issue apache/accumulo#1953 this commit contains changes to allow fast failure and achieve consistent behavior when a table or namespace name is invalid. --- .../java/org/apache/accumulo/core/Constants.java | 2 - .../accumulo/core/clientImpl/ClientContext.java | 33 ++++ .../accumulo/core/clientImpl/ConnectorImpl.java | 29 +++ .../core/clientImpl/NamespaceOperationsImpl.java | 56 +++++- .../accumulo/core/clientImpl/Namespaces.java | 5 +- .../core/clientImpl/SecurityOperationsImpl.java | 26 +++ .../core/clientImpl/TableOperationsHelper.java | 38 ++++ .../core/clientImpl/TableOperationsImpl.java | 216 ++++++++++++++++----- .../apache/accumulo/core/clientImpl/Tables.java | 24 ++- .../accumulo/manager/FateServiceHandler.java | 4 +- .../accumulo/manager/util/TableValidators.java | 4 +- .../shell/commands/CreateTableCommand.java | 2 +- .../org/apache/accumulo/test/NamespacesIT.java | 2 +- .../apache/accumulo/test/TableOperationsIT.java | 2 +- 14 files changed, 367 insertions(+), 76 deletions(-) diff --git a/core/src/main/java/org/apache/accumulo/core/Constants.java b/core/src/main/java/org/apache/accumulo/core/Constants.java index e2fac29..8559656 100644 --- a/core/src/main/java/org/apache/accumulo/core/Constants.java +++ b/core/src/main/java/org/apache/accumulo/core/Constants.java @@ -117,6 +117,4 @@ public class Constants { public static final int DEFAULT_VISIBILITY_CACHE_SIZE = 1000; - public static final int MAX_TABLE_NAME_LEN = 1024; - public static final int MAX_NAMESPACE_LEN = 1024; } diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java index 6219188..60c0c5b 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/ClientContext.java @@ -98,6 +98,7 @@ public class ClientContext implements AccumuloClient { private final ClientInfo info; private String instanceId; private final ZooCache zooCache; + private static final String VALID_TABLENAME_REGEX = "^(\\w{1,1024}[.])?(\\w{1,1024})$"; private Credentials creds; private BatchWriterConfig batchWriterConfig; @@ -509,6 +510,10 @@ public class ClientContext implements AccumuloClient { } TableId getTableId(String tableName) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + TableId tableId = Tables.getTableId(this, tableName); if (Tables.getTableState(this, tableId) == TableState.OFFLINE) throw new TableOfflineException(Tables.getTableOfflineMsg(this, tableId)); @@ -520,6 +525,10 @@ public class ClientContext implements AccumuloClient { int numQueryThreads) throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); checkArgument(authorizations != null, "authorizations is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + ensureOpen(); return new TabletServerBatchReader(this, getTableId(tableName), authorizations, numQueryThreads); @@ -547,6 +556,10 @@ public class ClientContext implements AccumuloClient { int numQueryThreads, BatchWriterConfig config) throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); checkArgument(authorizations != null, "authorizations is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + ensureOpen(); return new TabletServerBatchDeleter(this, getTableId(tableName), authorizations, numQueryThreads, config.merge(getBatchWriterConfig())); @@ -563,6 +576,10 @@ public class ClientContext implements AccumuloClient { public BatchWriter createBatchWriter(String tableName, BatchWriterConfig config) throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + ensureOpen(); // we used to allow null inputs for bw config if (config == null) { @@ -573,6 +590,10 @@ public class ClientContext implements AccumuloClient { @Override public BatchWriter createBatchWriter(String tableName) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return createBatchWriter(tableName, new BatchWriterConfig()); } @@ -590,6 +611,10 @@ public class ClientContext implements AccumuloClient { @Override public ConditionalWriter createConditionalWriter(String tableName, ConditionalWriterConfig config) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + ensureOpen(); if (config == null) { config = new ConditionalWriterConfig(); @@ -609,6 +634,10 @@ public class ClientContext implements AccumuloClient { throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); checkArgument(authorizations != null, "authorizations is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + ensureOpen(); Scanner scanner = new ScannerImpl(this, getTableId(tableName), authorizations); Integer batchSize = ClientProperty.SCANNER_BATCH_SIZE.getInteger(getProperties()); @@ -621,6 +650,10 @@ public class ClientContext implements AccumuloClient { @Override public Scanner createScanner(String tableName) throws TableNotFoundException, AccumuloSecurityException, AccumuloException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + Authorizations auths = securityOperations().getUserAuthorizations(getPrincipal()); return createScanner(tableName, auths); } diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/ConnectorImpl.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/ConnectorImpl.java index b609611..3ffe85f 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/ConnectorImpl.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/ConnectorImpl.java @@ -54,6 +54,7 @@ public class ConnectorImpl extends org.apache.accumulo.core.client.Connector { private static final String SYSTEM_TOKEN_NAME = "org.apache.accumulo.server.security.SystemCredentials$SystemToken"; private final ClientContext context; + private static final String VALID_TABLENAME_REGEX = "^(\\w{1,1024}[.])?(\\w{1,1024})$"; public ConnectorImpl(ClientContext context) throws AccumuloSecurityException, AccumuloException { this.context = context; @@ -85,6 +86,10 @@ public class ConnectorImpl extends org.apache.accumulo.core.client.Connector { @Override public BatchScanner createBatchScanner(String tableName, Authorizations authorizations, int numQueryThreads) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return context.createBatchScanner(tableName, authorizations, numQueryThreads); } @@ -94,6 +99,10 @@ public class ConnectorImpl extends org.apache.accumulo.core.client.Connector { throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); checkArgument(authorizations != null, "authorizations is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return new TabletServerBatchDeleter(context, context.getTableId(tableName), authorizations, numQueryThreads, new BatchWriterConfig().setMaxMemory(maxMemory) .setMaxLatency(maxLatency, TimeUnit.MILLISECONDS).setMaxWriteThreads(maxWriteThreads)); @@ -102,6 +111,10 @@ public class ConnectorImpl extends org.apache.accumulo.core.client.Connector { @Override public BatchDeleter createBatchDeleter(String tableName, Authorizations authorizations, int numQueryThreads, BatchWriterConfig config) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return context.createBatchDeleter(tableName, authorizations, numQueryThreads, config); } @@ -109,6 +122,10 @@ public class ConnectorImpl extends org.apache.accumulo.core.client.Connector { public BatchWriter createBatchWriter(String tableName, long maxMemory, long maxLatency, int maxWriteThreads) throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return new BatchWriterImpl(context, context.getTableId(tableName), new BatchWriterConfig().setMaxMemory(maxMemory) .setMaxLatency(maxLatency, TimeUnit.MILLISECONDS).setMaxWriteThreads(maxWriteThreads)); @@ -117,6 +134,10 @@ public class ConnectorImpl extends org.apache.accumulo.core.client.Connector { @Override public BatchWriter createBatchWriter(String tableName, BatchWriterConfig config) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return context.createBatchWriter(tableName, config); } @@ -135,12 +156,20 @@ public class ConnectorImpl extends org.apache.accumulo.core.client.Connector { @Override public ConditionalWriter createConditionalWriter(String tableName, ConditionalWriterConfig config) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return context.createConditionalWriter(tableName, config); } @Override public Scanner createScanner(String tableName, Authorizations authorizations) throws TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return context.createScanner(tableName, authorizations); } diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/NamespaceOperationsImpl.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/NamespaceOperationsImpl.java index 678cee0..a33482c 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/NamespaceOperationsImpl.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/NamespaceOperationsImpl.java @@ -20,8 +20,6 @@ package org.apache.accumulo.core.clientImpl; import static com.google.common.base.Preconditions.checkArgument; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.accumulo.core.Constants.MAX_NAMESPACE_LEN; -import static org.apache.accumulo.core.Constants.MAX_TABLE_NAME_LEN; import java.nio.ByteBuffer; import java.util.Arrays; @@ -65,6 +63,7 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { private TableOperationsImpl tableOps; private static final Logger log = LoggerFactory.getLogger(TableOperations.class); + private static final String VALID_NAMESPACE_REGEX = "^\\w{0,1024}$"; public NamespaceOperationsImpl(ClientContext context, TableOperationsImpl tableOps) { checkArgument(context != null, "context is null"); @@ -96,6 +95,9 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { @Override public boolean exists(String namespace) { checkArgument(namespace != null, "namespace is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); OpTimer timer = null; @@ -120,8 +122,10 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { public void create(String namespace) throws AccumuloException, AccumuloSecurityException, NamespaceExistsException { checkArgument(namespace != null, "namespace is null"); - checkArgument(namespace.length() <= MAX_NAMESPACE_LEN, - "Namespace is longer than " + MAX_NAMESPACE_LEN + " characters"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { doNamespaceFateOperation(FateOperation.NAMESPACE_CREATE, Arrays.asList(ByteBuffer.wrap(namespace.getBytes(UTF_8))), Collections.emptyMap(), @@ -136,8 +140,11 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { public void delete(String namespace) throws AccumuloException, AccumuloSecurityException, NamespaceNotFoundException, NamespaceNotEmptyException { checkArgument(namespace != null, "namespace is null"); - NamespaceId namespaceId = Namespaces.getNamespaceId(context, namespace); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + NamespaceId namespaceId = Namespaces.getNamespaceId(context, namespace); if (namespaceId.equals(Namespace.ACCUMULO.id()) || namespaceId.equals(Namespace.DEFAULT.id())) { Credentials credentials = context.getCredentials(); log.debug("{} attempted to delete the {} namespace", credentials.getPrincipal(), namespaceId); @@ -165,8 +172,14 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { public void rename(String oldNamespaceName, String newNamespaceName) throws AccumuloSecurityException, NamespaceNotFoundException, AccumuloException, NamespaceExistsException { - checkArgument(newNamespaceName.length() <= MAX_TABLE_NAME_LEN, - "Namespace is longer than " + MAX_TABLE_NAME_LEN + " characters"); + checkArgument(oldNamespaceName.matches(VALID_NAMESPACE_REGEX), + "oldNamespaceName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + + checkArgument(newNamespaceName.matches(VALID_NAMESPACE_REGEX), + "newNamespaceName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(oldNamespaceName.getBytes(UTF_8)), ByteBuffer.wrap(newNamespaceName.getBytes(UTF_8))); Map<String,String> opts = new HashMap<>(); @@ -179,6 +192,9 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { checkArgument(namespace != null, "namespace is null"); checkArgument(property != null, "property is null"); checkArgument(value != null, "value is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); ManagerClient.executeNamespace(context, client -> client.setNamespaceProperty(TraceUtil.traceInfo(), context.rpcCreds(), namespace, @@ -191,6 +207,9 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { throws AccumuloException, AccumuloSecurityException, NamespaceNotFoundException { checkArgument(namespace != null, "namespace is null"); checkArgument(property != null, "property is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); ManagerClient.executeNamespace(context, client -> client .removeNamespaceProperty(TraceUtil.traceInfo(), context.rpcCreds(), namespace, property)); @@ -201,6 +220,10 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { public Map<String,String> getConfiguration(final String namespace) throws AccumuloException, NamespaceNotFoundException { checkArgument(namespace != null, "namespace is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { return ServerClient.executeRaw(context, client -> client .getNamespaceConfiguration(TraceUtil.traceInfo(), context.rpcCreds(), namespace)); @@ -234,6 +257,9 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { checkArgument(namespace != null, "namespace is null"); checkArgument(className != null, "className is null"); checkArgument(asTypeName != null, "asTypeName is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); try { return ServerClient.executeRaw(context, @@ -259,6 +285,10 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { public void attachIterator(String namespace, IteratorSetting setting, EnumSet<IteratorScope> scopes) throws AccumuloSecurityException, AccumuloException, NamespaceNotFoundException { + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + testClassLoad(namespace, setting.getIteratorClass(), SortedKeyValueIterator.class.getName()); super.attachIterator(namespace, setting, scopes); } @@ -266,6 +296,10 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { @Override public int addConstraint(String namespace, String constraintClassName) throws AccumuloException, AccumuloSecurityException, NamespaceNotFoundException { + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + testClassLoad(namespace, constraintClassName, Constraint.class.getName()); return super.addConstraint(namespace, constraintClassName); } @@ -273,6 +307,10 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { private String doNamespaceFateOperation(FateOperation op, List<ByteBuffer> args, Map<String,String> opts, String namespace) throws AccumuloSecurityException, AccumuloException, NamespaceExistsException, NamespaceNotFoundException { + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { return tableOps.doFateOperation(op, args, opts, namespace); } catch (TableExistsException | TableNotFoundException e) { @@ -283,6 +321,10 @@ public class NamespaceOperationsImpl extends NamespaceOperationsHelper { private void checkLocalityGroups(String namespace, String propChanged) throws AccumuloException, NamespaceNotFoundException { + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + if (LocalityGroupUtil.isLocalityGroupProperty(propChanged)) { Map<String,String> allProps = getConfiguration(namespace); try { diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java index a365876..74d3411 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/Namespaces.java @@ -40,11 +40,11 @@ import org.slf4j.LoggerFactory; public class Namespaces { private static final Logger log = LoggerFactory.getLogger(Namespaces.class); - public static final String VALID_NAME_REGEX = "^\\w*$"; + public static final String VALID_NAMESPACE_REGEX = "^\\w{0,1024}$"; public static final Validator<String> VALID_NAME = new Validator<>() { @Override public boolean test(String namespace) { - return namespace != null && namespace.matches(VALID_NAME_REGEX); + return namespace != null && namespace.matches(VALID_NAMESPACE_REGEX); } @Override @@ -55,7 +55,6 @@ public class Namespaces { + namespace; } }; - public static final Validator<String> NOT_DEFAULT = new Validator<>() { @Override public boolean test(String namespace) { diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/SecurityOperationsImpl.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/SecurityOperationsImpl.java index 94a6dbf..fabb446 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/SecurityOperationsImpl.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/SecurityOperationsImpl.java @@ -49,6 +49,7 @@ import org.apache.accumulo.core.util.ByteBufferUtil; public class SecurityOperationsImpl implements SecurityOperations { private final ClientContext context; + private static final String VALID_NAMESPACE_REGEX = "^\\w{0,1024}$"; private void executeVoid(ClientExec<ClientService.Client> exec) throws AccumuloException, AccumuloSecurityException { @@ -104,6 +105,7 @@ public class SecurityOperationsImpl implements SecurityOperations { if (context.getSaslParams() == null) { checkArgument(password != null, "password is null"); } + executeVoid(client -> { if (context.getSaslParams() == null) { client.createLocalUser(TraceUtil.traceInfo(), context.rpcCreds(), principal, @@ -119,6 +121,7 @@ public class SecurityOperationsImpl implements SecurityOperations { public void dropLocalUser(final String principal) throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); + executeVoid( client -> client.dropLocalUser(TraceUtil.traceInfo(), context.rpcCreds(), principal)); } @@ -128,6 +131,7 @@ public class SecurityOperationsImpl implements SecurityOperations { throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); checkArgument(token != null, "token is null"); + final Credentials toAuth = new Credentials(principal, token); return execute(client -> client.authenticateUser(TraceUtil.traceInfo(), context.rpcCreds(), toAuth.toThrift(context.getInstanceID()))); @@ -138,6 +142,7 @@ public class SecurityOperationsImpl implements SecurityOperations { throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); checkArgument(token != null, "token is null"); + final Credentials toChange = new Credentials(principal, token); executeVoid(client -> client.changeLocalUserPassword(TraceUtil.traceInfo(), context.rpcCreds(), principal, ByteBuffer.wrap(token.getPassword()))); @@ -151,6 +156,7 @@ public class SecurityOperationsImpl implements SecurityOperations { throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); checkArgument(authorizations != null, "authorizations is null"); + executeVoid(client -> client.changeAuthorizations(TraceUtil.traceInfo(), context.rpcCreds(), principal, ByteBufferUtil.toByteBuffers(authorizations.getAuthorizations()))); } @@ -159,6 +165,7 @@ public class SecurityOperationsImpl implements SecurityOperations { public Authorizations getUserAuthorizations(final String principal) throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); + return execute(client -> new Authorizations( client.getUserAuthorizations(TraceUtil.traceInfo(), context.rpcCreds(), principal))); } @@ -168,6 +175,7 @@ public class SecurityOperationsImpl implements SecurityOperations { throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); checkArgument(perm != null, "perm is null"); + return execute(client -> client.hasSystemPermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, perm.getId())); } @@ -178,6 +186,7 @@ public class SecurityOperationsImpl implements SecurityOperations { checkArgument(principal != null, "principal is null"); checkArgument(table != null, "table is null"); checkArgument(perm != null, "perm is null"); + try { return execute(client -> client.hasTablePermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, table, perm.getId())); @@ -195,6 +204,10 @@ public class SecurityOperationsImpl implements SecurityOperations { checkArgument(principal != null, "principal is null"); checkArgument(namespace != null, "namespace is null"); checkArgument(permission != null, "permission is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return execute(client -> client.hasNamespacePermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, namespace, permission.getId())); } @@ -204,6 +217,7 @@ public class SecurityOperationsImpl implements SecurityOperations { throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); checkArgument(permission != null, "permission is null"); + executeVoid(client -> client.grantSystemPermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, permission.getId())); } @@ -214,6 +228,7 @@ public class SecurityOperationsImpl implements SecurityOperations { checkArgument(principal != null, "principal is null"); checkArgument(table != null, "table is null"); checkArgument(permission != null, "permission is null"); + try { executeVoid(client -> client.grantTablePermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, table, permission.getId())); @@ -231,6 +246,10 @@ public class SecurityOperationsImpl implements SecurityOperations { checkArgument(principal != null, "principal is null"); checkArgument(namespace != null, "namespace is null"); checkArgument(permission != null, "permission is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + executeVoid(client -> client.grantNamespacePermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, namespace, permission.getId())); } @@ -240,6 +259,7 @@ public class SecurityOperationsImpl implements SecurityOperations { throws AccumuloException, AccumuloSecurityException { checkArgument(principal != null, "principal is null"); checkArgument(permission != null, "permission is null"); + executeVoid(client -> client.revokeSystemPermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, permission.getId())); } @@ -250,6 +270,7 @@ public class SecurityOperationsImpl implements SecurityOperations { checkArgument(principal != null, "principal is null"); checkArgument(table != null, "table is null"); checkArgument(permission != null, "permission is null"); + try { executeVoid(client -> client.revokeTablePermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, table, permission.getId())); @@ -267,6 +288,10 @@ public class SecurityOperationsImpl implements SecurityOperations { checkArgument(principal != null, "principal is null"); checkArgument(namespace != null, "namespace is null"); checkArgument(permission != null, "permission is null"); + checkArgument(namespace.matches(VALID_NAMESPACE_REGEX), + "namespace must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + executeVoid(client -> client.revokeNamespacePermission(TraceUtil.traceInfo(), context.rpcCreds(), principal, namespace, permission.getId())); } @@ -280,6 +305,7 @@ public class SecurityOperationsImpl implements SecurityOperations { public DelegationToken getDelegationToken(DelegationTokenConfig cfg) throws AccumuloException, AccumuloSecurityException { final TDelegationTokenConfig tConfig; + if (cfg != null) { tConfig = DelegationTokenConfigSerializer.serialize(cfg); } else { diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsHelper.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsHelper.java index 28c6307..9843346 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsHelper.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsHelper.java @@ -37,9 +37,15 @@ import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope; public abstract class TableOperationsHelper implements TableOperations { + public static final String VALID_TABLENAME_REGEX = "^(\\w{1,1024}[.])?(\\w{1,1024})$"; + @Override public void attachIterator(String tableName, IteratorSetting setting) throws AccumuloSecurityException, AccumuloException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + attachIterator(tableName, setting, EnumSet.allOf(IteratorScope.class)); } @@ -50,7 +56,11 @@ public abstract class TableOperationsHelper implements TableOperations { checkArgument(tableName != null, "tableName is null"); checkArgument(setting != null, "setting is null"); checkArgument(scopes != null, "scopes is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); checkIteratorConflicts(tableName, setting, scopes); + for (IteratorScope scope : scopes) { String root = String.format("%s%s.%s", Property.TABLE_ITERATOR_PREFIX, scope.name().toLowerCase(), setting.getName()); @@ -64,6 +74,10 @@ public abstract class TableOperationsHelper implements TableOperations { @Override public void removeIterator(String tableName, String name, EnumSet<IteratorScope> scopes) throws AccumuloSecurityException, AccumuloException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + Map<String,String> copy = Map.copyOf(this.getConfiguration(tableName)); for (IteratorScope scope : scopes) { String root = String.format("%s%s.%s", Property.TABLE_ITERATOR_PREFIX, @@ -81,6 +95,10 @@ public abstract class TableOperationsHelper implements TableOperations { checkArgument(tableName != null, "tableName is null"); checkArgument(name != null, "name is null"); checkArgument(scope != null, "scope is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + int priority = -1; String classname = null; Map<String,String> settings = new HashMap<>(); @@ -109,6 +127,10 @@ public abstract class TableOperationsHelper implements TableOperations { @Override public Map<String,EnumSet<IteratorScope>> listIterators(String tableName) throws AccumuloException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + Map<String,EnumSet<IteratorScope>> result = new TreeMap<>(); for (Entry<String,String> property : this.getProperties(tableName)) { String name = property.getKey(); @@ -168,6 +190,10 @@ public abstract class TableOperationsHelper implements TableOperations { public void checkIteratorConflicts(String tableName, IteratorSetting setting, EnumSet<IteratorScope> scopes) throws AccumuloException, TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + Map<String,String> iteratorProps = Map.copyOf(this.getConfiguration(tableName)); checkIteratorConflicts(iteratorProps, setting, scopes); } @@ -175,6 +201,10 @@ public abstract class TableOperationsHelper implements TableOperations { @Override public int addConstraint(String tableName, String constraintClassName) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + TreeSet<Integer> constraintNumbers = new TreeSet<>(); TreeMap<String,Integer> constraintClasses = new TreeMap<>(); int i; @@ -204,12 +234,20 @@ public abstract class TableOperationsHelper implements TableOperations { @Override public void removeConstraint(String tableName, int number) throws AccumuloException, AccumuloSecurityException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + this.removeProperty(tableName, Property.TABLE_CONSTRAINT_PREFIX.toString() + number); } @Override public Map<String,Integer> listConstraints(String tableName) throws AccumuloException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + Map<String,Integer> constraints = new TreeMap<>(); for (Entry<String,String> property : this.getProperties(tableName)) { if (property.getKey().startsWith(Property.TABLE_CONSTRAINT_PREFIX.toString())) { diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java index 2631eed..52ae9e2 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/TableOperationsImpl.java @@ -24,7 +24,6 @@ import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toSet; -import static org.apache.accumulo.core.Constants.MAX_TABLE_NAME_LEN; import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.LOCATION; import static org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType.PREV_ROW; import static org.apache.accumulo.fate.util.UtilWaitThread.sleepUninterruptibly; @@ -156,6 +155,8 @@ import com.google.common.base.Preconditions; public class TableOperationsImpl extends TableOperationsHelper { public static final String CLONE_EXCLUDE_PREFIX = "!"; + public static final String VALID_TABLENAME_REGEX = "^(\\w{1,1024}[.])?(\\w{1,1024})$"; + private static final Logger log = LoggerFactory.getLogger(TableOperations.class); private final ClientContext context; @@ -188,6 +189,10 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public boolean exists(String tableName) { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + if (tableName.equals(MetadataTable.NAME) || tableName.equals(RootTable.NAME)) return true; @@ -212,6 +217,10 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void create(String tableName) throws AccumuloException, AccumuloSecurityException, TableExistsException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + create(tableName, new NewTableConfiguration()); } @@ -220,8 +229,9 @@ public class TableOperationsImpl extends TableOperationsHelper { throws AccumuloException, AccumuloSecurityException, TableExistsException { checkArgument(tableName != null, "tableName is null"); checkArgument(ntc != null, "ntc is null"); - checkArgument(tableName.length() <= MAX_TABLE_NAME_LEN, - "Table name is longer than " + MAX_TABLE_NAME_LEN + " characters"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); List<ByteBuffer> args = new ArrayList<>(); args.add(ByteBuffer.wrap(tableName.getBytes(UTF_8))); @@ -269,8 +279,8 @@ public class TableOperationsImpl extends TableOperationsHelper { } } - // This method is for retrying in the case of network failures; anything else it passes to the - // caller to deal with + // This method is for retrying in the case of network failures; + // anything else it passes to the caller to deal with private void executeFateOperation(long opid, FateOperation op, List<ByteBuffer> args, Map<String,String> opts, boolean autoCleanUp) throws ThriftSecurityException, TException, ThriftTableOperationException { @@ -336,6 +346,10 @@ public class TableOperationsImpl extends TableOperationsHelper { public String doBulkFateOperation(List<ByteBuffer> args, String tableName) throws AccumuloSecurityException, AccumuloException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { return doFateOperation(FateOperation.TABLE_BULK_IMPORT2, args, Collections.emptyMap(), tableName); @@ -453,9 +467,8 @@ public class TableOperationsImpl extends TableOperationsHelper { int mid = splits.size() / 2; - // split the middle split point to ensure that child task split different tablets and can - // therefore - // run in parallel + // split the middle split point to ensure that child task split + // different tablets and can therefore run in parallel addSplits(env.tableName, new TreeSet<>(splits.subList(mid, mid + 1)), env.tableId); env.latch.countDown(); @@ -472,13 +485,16 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void addSplits(String tableName, SortedSet<Text> partitionKeys) throws TableNotFoundException, AccumuloException, AccumuloSecurityException { - TableId tableId = Tables.getTableId(context, tableName); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + TableId tableId = Tables.getTableId(context, tableName); List<Text> splits = new ArrayList<>(partitionKeys); - // should be sorted because we copied from a sorted set, but that makes assumptions about - // how the copy was done so resort to be sure. - Collections.sort(splits); + // should be sorted because we copied from a sorted set, but that makes + // assumptions about how the copy was done so resort to be sure. + Collections.sort(splits); CountDownLatch latch = new CountDownLatch(splits.size()); AtomicReference<Exception> exception = new AtomicReference<>(null); @@ -526,8 +542,11 @@ public class TableOperationsImpl extends TableOperationsHelper { private void addSplits(String tableName, SortedSet<Text> partitionKeys, TableId tableId) throws AccumuloException, AccumuloSecurityException, TableNotFoundException, AccumuloServerException { - TabletLocator tabLocator = TabletLocator.getLocator(context, tableId); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + TabletLocator tabLocator = TabletLocator.getLocator(context, tableId); for (Text split : partitionKeys) { boolean successful = false; int attempt = 0; @@ -613,8 +632,11 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void merge(String tableName, Text start, Text end) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { - checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + ByteBuffer EMPTY = ByteBuffer.allocate(0); List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(tableName.getBytes(UTF_8)), start == null ? EMPTY : TextUtil.getByteBuffer(start), @@ -632,8 +654,11 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void deleteRows(String tableName, Text start, Text end) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { - checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + ByteBuffer EMPTY = ByteBuffer.allocate(0); List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(tableName.getBytes(UTF_8)), start == null ? EMPTY : TextUtil.getByteBuffer(start), @@ -651,17 +676,22 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public Collection<Text> listSplits(String tableName) throws TableNotFoundException, AccumuloSecurityException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return _listSplits(tableName); } private List<Text> _listSplits(String tableName) throws TableNotFoundException, AccumuloSecurityException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); TableId tableId = Tables.getTableId(context, tableName); - TreeMap<KeyExtent,String> tabletLocations = new TreeMap<>(); - while (true) { try { tabletLocations.clear(); @@ -685,7 +715,6 @@ public class TableOperationsImpl extends TableOperationsHelper { } ArrayList<Text> endRows = new ArrayList<>(tabletLocations.size()); - for (KeyExtent ke : tabletLocations.keySet()) if (ke.endRow() != null) endRows.add(ke.endRow()); @@ -697,16 +726,17 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public Collection<Text> listSplits(String tableName, int maxSplits) throws TableNotFoundException, AccumuloSecurityException { - List<Text> endRows = _listSplits(tableName); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + List<Text> endRows = _listSplits(tableName); if (endRows.size() <= maxSplits) return endRows; double r = (maxSplits + 1) / (double) (endRows.size()); double pos = 0; - ArrayList<Text> subset = new ArrayList<>(maxSplits); - int j = 0; for (int i = 0; i < endRows.size() && j < maxSplits; i++) { pos += r; @@ -724,10 +754,12 @@ public class TableOperationsImpl extends TableOperationsHelper { public void delete(String tableName) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(tableName.getBytes(UTF_8))); Map<String,String> opts = new HashMap<>(); - try { doTableFateOperation(tableName, TableNotFoundException.class, FateOperation.TABLE_DELETE, args, opts); @@ -755,8 +787,13 @@ public class TableOperationsImpl extends TableOperationsHelper { checkArgument(srcTableName != null, "srcTableName is null"); checkArgument(newTableName != null, "newTableName is null"); - checkArgument(newTableName.length() <= MAX_TABLE_NAME_LEN, - "Table name is longer than " + MAX_TABLE_NAME_LEN + " characters"); + checkArgument(srcTableName.matches(VALID_TABLENAME_REGEX), + "srcTableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + + checkArgument(newTableName.matches(VALID_TABLENAME_REGEX), + "newTableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); TableId srcTableId = Tables.getTableId(context, srcTableName); @@ -792,8 +829,14 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void rename(String oldTableName, String newTableName) throws AccumuloSecurityException, TableNotFoundException, AccumuloException, TableExistsException { - checkArgument(newTableName.length() <= MAX_TABLE_NAME_LEN, - "Table name is longer than " + MAX_TABLE_NAME_LEN + " characters"); + checkArgument(oldTableName.matches(VALID_TABLENAME_REGEX), + "oldTableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + + checkArgument(newTableName.matches(VALID_TABLENAME_REGEX), + "newTableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(oldTableName.getBytes(UTF_8)), ByteBuffer.wrap(newTableName.getBytes(UTF_8))); Map<String,String> opts = new HashMap<>(); @@ -803,6 +846,10 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void flush(String tableName) throws AccumuloException, AccumuloSecurityException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { flush(tableName, null, null, false); } catch (TableNotFoundException e) { @@ -814,6 +861,9 @@ public class TableOperationsImpl extends TableOperationsHelper { public void flush(String tableName, Text start, Text end, boolean wait) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); TableId tableId = Tables.getTableId(context, tableName); _flush(tableId, start, end, wait); @@ -837,6 +887,9 @@ public class TableOperationsImpl extends TableOperationsHelper { public void compact(String tableName, CompactionConfig config) throws AccumuloSecurityException, TableNotFoundException, AccumuloException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); // Ensure compaction iterators exist on a tabletserver final String skviName = SortedKeyValueIterator.class.getName(); @@ -868,7 +921,6 @@ public class TableOperationsImpl extends TableOperationsHelper { } TableId tableId = Tables.getTableId(context, tableName); - Text start = config.getStartRow(); Text end = config.getEndRow(); @@ -877,8 +929,8 @@ public class TableOperationsImpl extends TableOperationsHelper { List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(tableId.canonical().getBytes(UTF_8)), ByteBuffer.wrap(UserCompactionUtils.encode(config))); - Map<String,String> opts = new HashMap<>(); + try { doFateOperation(FateOperation.TABLE_COMPACT, args, opts, tableName, config.getWait()); } catch (TableExistsException | NamespaceExistsException e) { @@ -905,11 +957,14 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void cancelCompaction(String tableName) throws AccumuloSecurityException, TableNotFoundException, AccumuloException { - TableId tableId = Tables.getTableId(context, tableName); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + TableId tableId = Tables.getTableId(context, tableName); List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(tableId.canonical().getBytes(UTF_8))); - Map<String,String> opts = new HashMap<>(); + try { doTableFateOperation(tableName, TableNotFoundException.class, FateOperation.TABLE_CANCEL_COMPACT, args, opts); @@ -922,7 +977,6 @@ public class TableOperationsImpl extends TableOperationsHelper { private void _flush(TableId tableId, Text start, Text end, boolean wait) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { - try { long flushID; @@ -994,6 +1048,10 @@ public class TableOperationsImpl extends TableOperationsHelper { checkArgument(tableName != null, "tableName is null"); checkArgument(property != null, "property is null"); checkArgument(value != null, "value is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { setPropertyNoChecks(tableName, property, value); @@ -1015,6 +1073,10 @@ public class TableOperationsImpl extends TableOperationsHelper { throws AccumuloException, AccumuloSecurityException { checkArgument(tableName != null, "tableName is null"); checkArgument(property != null, "property is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { removePropertyNoChecks(tableName, property); @@ -1051,6 +1113,10 @@ public class TableOperationsImpl extends TableOperationsHelper { public Map<String,String> getConfiguration(final String tableName) throws AccumuloException, TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + try { return ServerClient.executeRaw(context, client -> client .getTableConfiguration(TraceUtil.traceInfo(), context.rpcCreds(), tableName)); @@ -1111,10 +1177,11 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public Map<String,Set<Text>> getLocalityGroups(String tableName) throws AccumuloException, TableNotFoundException { + AccumuloConfiguration conf = new ConfigurationCopy(this.getProperties(tableName)); Map<String,Set<ByteSequence>> groups = LocalityGroupUtil.getLocalityGroups(conf); - Map<String,Set<Text>> groups2 = new HashMap<>(); + for (Entry<String,Set<ByteSequence>> entry : groups.entrySet()) { HashSet<Text> colFams = new HashSet<>(); @@ -1134,6 +1201,10 @@ public class TableOperationsImpl extends TableOperationsHelper { throws AccumuloException, AccumuloSecurityException, TableNotFoundException { checkArgument(tableName != null, "tableName is null"); checkArgument(range != null, "range is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + if (maxSplits < 1) throw new IllegalArgumentException("maximum splits must be >= 1"); if (maxSplits == 1) @@ -1225,9 +1296,12 @@ public class TableOperationsImpl extends TableOperationsHelper { checkArgument(tableName != null, "tableName is null"); checkArgument(dir != null, "dir is null"); checkArgument(failureDir != null, "failureDir is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + // check for table existence Tables.getTableId(context, tableName); - Path dirPath = checkPath(dir, "Bulk", ""); Path failPath = checkPath(failureDir, "Bulk", "failure"); @@ -1248,12 +1322,10 @@ public class TableOperationsImpl extends TableOperationsHelper { private void waitForTableStateTransition(TableId tableId, TableState expectedState) throws AccumuloException, TableNotFoundException { - Text startRow = null; Text lastRow = null; while (true) { - if (Tables.getTableState(context, tableId) != expectedState) { Tables.clearCache(context); TableState currentState = Tables.getTableState(context, tableId); @@ -1286,7 +1358,6 @@ public class TableOperationsImpl extends TableOperationsHelper { for (TabletMetadata tablet : tablets) { total++; - Location loc = tablet.getLocation(); if ((expectedState == TableState.ONLINE @@ -1352,8 +1423,11 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void offline(String tableName, boolean wait) throws AccumuloSecurityException, AccumuloException, TableNotFoundException { - checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + TableId tableId = Tables.getTableId(context, tableName); List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(tableId.canonical().getBytes(UTF_8))); Map<String,String> opts = new HashMap<>(); @@ -1373,9 +1447,11 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public boolean isOnline(String tableName) throws AccumuloException, TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); TableId tableId = Tables.getTableId(context, tableName); - TableState expectedState = Tables.getTableState(context, tableId, true); return expectedState == TableState.ONLINE; } @@ -1390,13 +1466,14 @@ public class TableOperationsImpl extends TableOperationsHelper { public void online(String tableName, boolean wait) throws AccumuloSecurityException, AccumuloException, TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); TableId tableId = Tables.getTableId(context, tableName); - /** * ACCUMULO-4574 if table is already online return without executing fate operation. */ - if (isOnline(tableName)) { if (wait) waitForTableStateTransition(tableId, TableState.ONLINE); @@ -1421,6 +1498,10 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void clearLocatorCache(String tableName) throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "Table name must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + TabletLocator tabLocator = TabletLocator.getLocator(context, Tables.getTableId(context, tableName)); tabLocator.invalidateCache(); @@ -1438,7 +1519,10 @@ public class TableOperationsImpl extends TableOperationsHelper { public Text getMaxRow(String tableName, Authorizations auths, Text startRow, boolean startInclusive, Text endRow, boolean endInclusive) throws TableNotFoundException { checkArgument(tableName != null, "tableName is null"); - checkArgument(auths != null, "auths is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + Scanner scanner = context.createScanner(tableName, auths); return FindMax.findMax(scanner, startRow, startInclusive, endRow, endInclusive); } @@ -1563,8 +1647,9 @@ public class TableOperationsImpl extends TableOperationsHelper { throws TableExistsException, AccumuloException, AccumuloSecurityException { checkArgument(tableName != null, "tableName is null"); checkArgument(importDirs != null, "importDir is null"); - checkArgument(tableName.length() <= MAX_TABLE_NAME_LEN, - "Table name is longer than " + MAX_TABLE_NAME_LEN + " characters"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); Set<String> checkedImportDirs = new HashSet<>(); try { @@ -1623,10 +1708,12 @@ public class TableOperationsImpl extends TableOperationsHelper { throws TableNotFoundException, AccumuloException, AccumuloSecurityException { checkArgument(tableName != null, "tableName is null"); checkArgument(exportDir != null, "exportDir is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); List<ByteBuffer> args = Arrays.asList(ByteBuffer.wrap(tableName.getBytes(UTF_8)), ByteBuffer.wrap(exportDir.getBytes(UTF_8))); - Map<String,String> opts = Collections.emptyMap(); try { @@ -1645,6 +1732,9 @@ public class TableOperationsImpl extends TableOperationsHelper { checkArgument(tableName != null, "tableName is null"); checkArgument(className != null, "className is null"); checkArgument(asTypeName != null, "asTypeName is null"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); try { return ServerClient.executeRaw(context, @@ -1709,6 +1799,10 @@ public class TableOperationsImpl extends TableOperationsHelper { private void clearSamplerOptions(String tableName) throws AccumuloException, TableNotFoundException, AccumuloSecurityException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + String prefix = Property.TABLE_SAMPLER_OPTS.getKey(); for (Entry<String,String> entry : getProperties(tableName)) { String property = entry.getKey(); @@ -1721,8 +1815,11 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void setSamplerConfiguration(String tableName, SamplerConfiguration samplerConfiguration) throws AccumuloException, TableNotFoundException, AccumuloSecurityException { - clearSamplerOptions(tableName); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + clearSamplerOptions(tableName); List<Pair<String,String>> props = new SamplerConfigurationImpl(samplerConfiguration).toTableProperties(); for (Pair<String,String> pair : props) { @@ -1733,6 +1830,10 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void clearSamplerConfiguration(String tableName) throws AccumuloException, TableNotFoundException, AccumuloSecurityException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + removeProperty(tableName, Property.TABLE_SAMPLER.getKey()); clearSamplerOptions(tableName); } @@ -1740,6 +1841,10 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public SamplerConfiguration getSamplerConfiguration(String tableName) throws TableNotFoundException, AccumuloException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + AccumuloConfiguration conf = new ConfigurationCopy(this.getProperties(tableName)); SamplerConfigurationImpl sci = SamplerConfigurationImpl.newSamplerConfig(conf); if (sci == null) { @@ -1810,6 +1915,9 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public Locations locate(String tableName, Collection<Range> ranges) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); requireNonNull(tableName, "tableName must be non null"); requireNonNull(ranges, "ranges must be non null"); @@ -1854,9 +1962,11 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public SummaryRetriever summaries(String tableName) { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); return new SummaryRetriever() { - private Text startRow = null; private Text endRow = null; private List<TSummarizerConfiguration> summariesToFetch = Collections.emptyList(); @@ -1957,12 +2067,15 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void addSummarizers(String tableName, SummarizerConfiguration... newConfigs) throws AccumuloException, AccumuloSecurityException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + HashSet<SummarizerConfiguration> currentConfigs = new HashSet<>(SummarizerConfiguration.fromTableProperties(getProperties(tableName))); HashSet<SummarizerConfiguration> newConfigSet = new HashSet<>(Arrays.asList(newConfigs)); newConfigSet.removeIf(currentConfigs::contains); - Set<String> newIds = newConfigSet.stream().map(SummarizerConfiguration::getPropertyId).collect(toSet()); @@ -1982,8 +2095,13 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public void removeSummarizers(String tableName, Predicate<SummarizerConfiguration> predicate) throws AccumuloException, TableNotFoundException, AccumuloSecurityException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + Collection<SummarizerConfiguration> summarizerConfigs = SummarizerConfiguration.fromTableProperties(getProperties(tableName)); + for (SummarizerConfiguration sc : summarizerConfigs) { if (predicate.test(sc)) { Set<String> ks = sc.toTableProperties().keySet(); @@ -1997,6 +2115,10 @@ public class TableOperationsImpl extends TableOperationsHelper { @Override public List<SummarizerConfiguration> listSummarizers(String tableName) throws AccumuloException, TableNotFoundException { + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "tableName must only contain word characters (letters, digits, and underscores)" + + " and cannot exceed 1024 characters"); + return new ArrayList<>(SummarizerConfiguration.fromTableProperties(getProperties(tableName))); } diff --git a/core/src/main/java/org/apache/accumulo/core/clientImpl/Tables.java b/core/src/main/java/org/apache/accumulo/core/clientImpl/Tables.java index 35f68ad..e49efb8 100644 --- a/core/src/main/java/org/apache/accumulo/core/clientImpl/Tables.java +++ b/core/src/main/java/org/apache/accumulo/core/clientImpl/Tables.java @@ -45,12 +45,12 @@ import com.google.common.cache.CacheBuilder; public class Tables { - public static final String VALID_NAME_REGEX = "^(\\w+\\.)?(\\w+)$"; + public static final String VALID_TABLENAME_REGEX = "^(\\w{1,1024}[.])?(\\w{1,1024})$"; private static final SecurityPermission TABLES_PERMISSION = new SecurityPermission("tablesPermission"); - // Per instance cache will expire after 10 minutes in case we encounter an instance not used - // frequently + // Per instance cache will expire after 10 minutes in case we + // encounter an instance not used frequently private static Cache<String,TableMap> instanceToMapCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build(); @@ -114,9 +114,10 @@ public class Tables { public static TableId _getTableId(ClientContext context, String tableName) throws NamespaceNotFoundException, TableNotFoundException { TableId tableId = getNameToIdMap(context).get(tableName); + if (tableId == null) { - // maybe the table exist, but the cache was not updated yet... so try to clear the cache and - // check again + // maybe the table exist, but the cache was not updated yet... + // so try to clear the cache and check again clearCache(context); tableId = getNameToIdMap(context).get(tableName); if (tableId == null) { @@ -133,6 +134,7 @@ public class Tables { public static String getTableName(ClientContext context, TableId tableId) throws TableNotFoundException { String tableName = getIdToNameMap(context).get(tableId); + if (tableName == null) throw new TableNotFoundException(tableId.canonical(), null, null); return tableName; @@ -141,6 +143,7 @@ public class Tables { public static String getTableOfflineMsg(ClientContext context, TableId tableId) { if (tableId == null) return "Table <unknown table> is offline"; + try { String tableName = Tables.getTableName(context, tableId); return "Table " + tableName + " (" + tableId.canonical() + ") is offline"; @@ -163,9 +166,7 @@ public class Tables { */ private static TableMap getTableMap(final ClientContext context) { TableMap map; - final ZooCache zc = getZooCache(context); - map = getTableMap(context, zc); if (!map.isCurrent(zc)) { @@ -212,6 +213,7 @@ public class Tables { public static String getPrintableTableInfoFromId(ClientContext context, TableId tableId) { String tableName = null; + try { tableName = getTableName(context, tableId); } catch (TableNotFoundException e) { @@ -223,6 +225,7 @@ public class Tables { public static String getPrintableTableInfoFromName(ClientContext context, String tableName) { TableId tableId = null; + try { tableId = getTableId(context, tableName); } catch (TableNotFoundException e) { @@ -250,7 +253,6 @@ public class Tables { */ public static TableState getTableState(ClientContext context, TableId tableId, boolean clearCachedState) { - String statePath = context.getZooKeeperRoot() + Constants.ZTABLES + "/" + tableId.canonical() + Constants.ZTABLE_STATE; @@ -273,6 +275,7 @@ public class Tables { public static String qualified(String tableName, String defaultNamespace) { Pair<String,String> qualifiedTableName = qualify(tableName, defaultNamespace); + if (Namespace.DEFAULT.name().equals(qualifiedTableName.getFirst())) return qualifiedTableName.getSecond(); else @@ -284,8 +287,9 @@ public class Tables { } public static Pair<String,String> qualify(String tableName, String defaultNamespace) { - if (!tableName.matches(VALID_NAME_REGEX)) - throw new IllegalArgumentException("Invalid table name '" + tableName + "'"); + checkArgument(tableName.matches(VALID_TABLENAME_REGEX), + "Invalid table name '" + tableName + "'"); + if (MetadataTable.OLD_NAME.equals(tableName)) tableName = MetadataTable.NAME; if (tableName.contains(".")) { diff --git a/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java b/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java index 36af428..8cf1f12 100644 --- a/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java +++ b/server/manager/src/main/java/org/apache/accumulo/manager/FateServiceHandler.java @@ -19,8 +19,6 @@ package org.apache.accumulo.manager; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.accumulo.core.Constants.MAX_NAMESPACE_LEN; -import static org.apache.accumulo.core.Constants.MAX_TABLE_NAME_LEN; import static org.apache.accumulo.manager.util.TableValidators.CAN_CLONE; import static org.apache.accumulo.manager.util.TableValidators.NOT_METADATA; import static org.apache.accumulo.manager.util.TableValidators.NOT_ROOT_ID; @@ -93,6 +91,8 @@ class FateServiceHandler implements FateService.Iface { protected final Manager manager; protected static final Logger log = Manager.log; + protected static final int MAX_TABLE_NAME_LEN = 1024; + protected static final int MAX_NAMESPACE_LEN = 1024; public FateServiceHandler(Manager manager) { this.manager = manager; diff --git a/server/manager/src/main/java/org/apache/accumulo/manager/util/TableValidators.java b/server/manager/src/main/java/org/apache/accumulo/manager/util/TableValidators.java index f0d39e4..4eefa80 100644 --- a/server/manager/src/main/java/org/apache/accumulo/manager/util/TableValidators.java +++ b/server/manager/src/main/java/org/apache/accumulo/manager/util/TableValidators.java @@ -18,7 +18,7 @@ */ package org.apache.accumulo.manager.util; -import static org.apache.accumulo.core.clientImpl.Tables.VALID_NAME_REGEX; +import static org.apache.accumulo.core.clientImpl.Tables.VALID_TABLENAME_REGEX; import static org.apache.accumulo.core.clientImpl.Tables.qualify; import java.util.Arrays; @@ -39,7 +39,7 @@ public class TableValidators { public static final Validator<String> VALID_NAME = new Validator<>() { @Override public boolean test(String tableName) { - return tableName != null && tableName.matches(VALID_NAME_REGEX); + return tableName != null && tableName.matches(VALID_TABLENAME_REGEX); } @Override diff --git a/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java b/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java index 41d8c2c..93a933c 100644 --- a/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java +++ b/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java @@ -74,7 +74,7 @@ public class CreateTableCommand extends Command { final String testTableName = cl.getArgs()[0]; NewTableConfiguration ntc = new NewTableConfiguration(); - if (!testTableName.matches(Tables.VALID_NAME_REGEX)) { + if (!testTableName.matches(Tables.VALID_TABLENAME_REGEX)) { shellState.getWriter() .println("Only letters, numbers and underscores are allowed for use in table names."); throw new IllegalArgumentException(); diff --git a/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java b/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java index b1d55df..d37dca3 100644 --- a/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java +++ b/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java @@ -18,7 +18,6 @@ */ package org.apache.accumulo.test; -import static org.apache.accumulo.core.Constants.MAX_NAMESPACE_LEN; import static org.apache.accumulo.fate.util.UtilWaitThread.sleepUninterruptibly; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -100,6 +99,7 @@ public class NamespacesIT extends SharedMiniClusterBase { private AccumuloClient c; private String namespace; + private static final int MAX_NAMESPACE_LEN = 1024; @BeforeClass public static void setup() throws Exception { diff --git a/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java b/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java index 3182e35..884f121 100644 --- a/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java +++ b/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java @@ -18,7 +18,6 @@ */ package org.apache.accumulo.test; -import static org.apache.accumulo.core.Constants.MAX_TABLE_NAME_LEN; import static org.apache.accumulo.fate.util.UtilWaitThread.sleepUninterruptibly; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -74,6 +73,7 @@ import com.google.common.collect.Sets; public class TableOperationsIT extends AccumuloClusterHarness { private AccumuloClient accumuloClient; + private static final int MAX_TABLE_NAME_LEN = 1024; @Override public int defaultTimeoutSeconds() {