| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.accumulo.master; |
| |
| import static org.apache.accumulo.master.util.TableValidators.NOT_METADATA; |
| import static org.apache.accumulo.master.util.TableValidators.NOT_ROOT_ID; |
| import static org.apache.accumulo.master.util.TableValidators.NOT_SYSTEM; |
| import static org.apache.accumulo.master.util.TableValidators.VALID_ID; |
| import static org.apache.accumulo.master.util.TableValidators.VALID_NAME; |
| |
| import java.nio.ByteBuffer; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import org.apache.accumulo.core.client.AccumuloSecurityException; |
| import org.apache.accumulo.core.client.IteratorSetting; |
| import org.apache.accumulo.core.client.NamespaceNotFoundException; |
| import org.apache.accumulo.core.client.TableNotFoundException; |
| import org.apache.accumulo.core.client.admin.CompactionStrategyConfig; |
| import org.apache.accumulo.core.client.admin.TimeType; |
| import org.apache.accumulo.core.client.impl.CompactionStrategyConfigUtil; |
| import org.apache.accumulo.core.client.impl.Namespaces; |
| import org.apache.accumulo.core.client.impl.TableOperationsImpl; |
| import org.apache.accumulo.core.client.impl.Tables; |
| import org.apache.accumulo.core.client.impl.thrift.SecurityErrorCode; |
| import org.apache.accumulo.core.client.impl.thrift.TableOperation; |
| import org.apache.accumulo.core.client.impl.thrift.TableOperationExceptionType; |
| import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException; |
| import org.apache.accumulo.core.client.impl.thrift.ThriftTableOperationException; |
| import org.apache.accumulo.core.iterators.IteratorUtil; |
| import org.apache.accumulo.core.master.thrift.BulkImportState; |
| import org.apache.accumulo.core.master.thrift.FateOperation; |
| import org.apache.accumulo.core.master.thrift.FateService; |
| import org.apache.accumulo.core.security.thrift.TCredentials; |
| import org.apache.accumulo.core.trace.thrift.TInfo; |
| import org.apache.accumulo.core.util.ByteBufferUtil; |
| import org.apache.accumulo.core.util.Validator; |
| import org.apache.accumulo.fate.ReadOnlyTStore.TStatus; |
| import org.apache.accumulo.master.tableOps.BulkImport; |
| import org.apache.accumulo.master.tableOps.CancelCompactions; |
| import org.apache.accumulo.master.tableOps.ChangeTableState; |
| import org.apache.accumulo.master.tableOps.CloneTable; |
| import org.apache.accumulo.master.tableOps.CompactRange; |
| import org.apache.accumulo.master.tableOps.CreateNamespace; |
| import org.apache.accumulo.master.tableOps.CreateTable; |
| import org.apache.accumulo.master.tableOps.DeleteNamespace; |
| import org.apache.accumulo.master.tableOps.DeleteTable; |
| import org.apache.accumulo.master.tableOps.ExportTable; |
| import org.apache.accumulo.master.tableOps.ImportTable; |
| import org.apache.accumulo.master.tableOps.RenameNamespace; |
| import org.apache.accumulo.master.tableOps.RenameTable; |
| import org.apache.accumulo.master.tableOps.TableRangeOp; |
| import org.apache.accumulo.master.tableOps.TraceRepo; |
| import org.apache.accumulo.server.client.ClientServiceHandler; |
| import org.apache.accumulo.server.master.state.MergeInfo; |
| import org.apache.accumulo.server.util.TablePropUtil; |
| import org.apache.hadoop.io.Text; |
| import org.slf4j.Logger; |
| |
| /** |
| * |
| */ |
| class FateServiceHandler implements FateService.Iface { |
| |
| protected final Master master; |
| protected static final Logger log = Master.log; |
| |
| public FateServiceHandler(Master master) { |
| this.master = master; |
| } |
| |
| @Override |
| public long beginFateOperation(TInfo tinfo, TCredentials credentials) throws ThriftSecurityException { |
| authenticate(credentials); |
| return master.fate.startTransaction(); |
| } |
| |
| @Override |
| public void executeFateOperation(TInfo tinfo, TCredentials c, long opid, FateOperation op, List<ByteBuffer> arguments, Map<String,String> options, |
| boolean autoCleanup) throws ThriftSecurityException, ThriftTableOperationException { |
| authenticate(c); |
| |
| switch (op) { |
| case NAMESPACE_CREATE: { |
| TableOperation tableOp = TableOperation.CREATE; |
| String namespace = validateNamespaceArgument(arguments.get(0), tableOp, null); |
| |
| if (!master.security.canCreateNamespace(c, namespace)) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new CreateNamespace(c.getPrincipal(), namespace, options)), autoCleanup); |
| break; |
| } |
| case NAMESPACE_RENAME: { |
| TableOperation tableOp = TableOperation.RENAME; |
| String oldName = validateNamespaceArgument(arguments.get(0), tableOp, Namespaces.NOT_DEFAULT.and(Namespaces.NOT_ACCUMULO)); |
| String newName = validateNamespaceArgument(arguments.get(1), tableOp, null); |
| |
| String namespaceId = ClientServiceHandler.checkNamespaceId(master.getInstance(), oldName, tableOp); |
| if (!master.security.canRenameNamespace(c, namespaceId, oldName, newName)) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new RenameNamespace(namespaceId, oldName, newName)), autoCleanup); |
| break; |
| } |
| case NAMESPACE_DELETE: { |
| TableOperation tableOp = TableOperation.DELETE; |
| String namespace = validateNamespaceArgument(arguments.get(0), tableOp, Namespaces.NOT_DEFAULT.and(Namespaces.NOT_ACCUMULO)); |
| |
| String namespaceId = ClientServiceHandler.checkNamespaceId(master.getInstance(), namespace, tableOp); |
| if (!master.security.canDeleteNamespace(c, namespaceId)) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new DeleteNamespace(namespaceId)), autoCleanup); |
| break; |
| } |
| case TABLE_CREATE: { |
| TableOperation tableOp = TableOperation.CREATE; |
| String tableName = validateTableNameArgument(arguments.get(0), tableOp, NOT_SYSTEM); |
| TimeType timeType = TimeType.valueOf(ByteBufferUtil.toString(arguments.get(1))); |
| |
| String namespaceId; |
| |
| try { |
| namespaceId = Namespaces.getNamespaceId(master.getInstance(), Tables.qualify(tableName).getFirst()); |
| } catch (NamespaceNotFoundException e) { |
| throw new ThriftTableOperationException(null, tableName, tableOp, TableOperationExceptionType.NAMESPACE_NOTFOUND, ""); |
| } |
| |
| if (!master.security.canCreateTable(c, tableName, namespaceId)) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new CreateTable(c.getPrincipal(), tableName, timeType, options, namespaceId)), autoCleanup); |
| |
| break; |
| } |
| case TABLE_RENAME: { |
| TableOperation tableOp = TableOperation.RENAME; |
| final String oldTableName = validateTableNameArgument(arguments.get(0), tableOp, NOT_SYSTEM); |
| String newTableName = validateTableNameArgument(arguments.get(1), tableOp, new Validator<String>() { |
| |
| @Override |
| public boolean apply(String argument) { |
| // verify they are in the same namespace |
| String oldNamespace = Tables.qualify(oldTableName).getFirst(); |
| return oldNamespace.equals(Tables.qualify(argument).getFirst()); |
| } |
| |
| @Override |
| public String invalidMessage(String argument) { |
| return "Cannot move tables to a new namespace by renaming. The namespace for " + oldTableName + " does not match " + argument; |
| } |
| |
| }); |
| |
| String tableId = ClientServiceHandler.checkTableId(master.getInstance(), oldTableName, tableOp); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canRename; |
| try { |
| canRename = master.security.canRenameTable(c, tableId, oldTableName, newTableName, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, oldTableName, TableOperation.RENAME); |
| throw e; |
| } |
| |
| if (!canRename) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| try { |
| master.fate.seedTransaction(opid, new TraceRepo<>(new RenameTable(tableId, oldTableName, newTableName)), autoCleanup); |
| } catch (NamespaceNotFoundException e) { |
| throw new ThriftTableOperationException(null, oldTableName, tableOp, TableOperationExceptionType.NAMESPACE_NOTFOUND, ""); |
| } |
| |
| break; |
| } |
| case TABLE_CLONE: { |
| TableOperation tableOp = TableOperation.CLONE; |
| String srcTableId = validateTableIdArgument(arguments.get(0), tableOp, NOT_ROOT_ID); |
| String tableName = validateTableNameArgument(arguments.get(1), tableOp, NOT_SYSTEM); |
| String namespaceId; |
| try { |
| namespaceId = Namespaces.getNamespaceId(master.getInstance(), Tables.qualify(tableName).getFirst()); |
| } catch (NamespaceNotFoundException e) { |
| // shouldn't happen, but possible once cloning between namespaces is supported |
| throw new ThriftTableOperationException(null, tableName, tableOp, TableOperationExceptionType.NAMESPACE_NOTFOUND, ""); |
| } |
| |
| final boolean canCloneTable; |
| try { |
| canCloneTable = master.security.canCloneTable(c, srcTableId, tableName, namespaceId, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, srcTableId, null, TableOperation.CLONE); |
| throw e; |
| } |
| |
| if (!canCloneTable) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| Map<String,String> propertiesToSet = new HashMap<>(); |
| Set<String> propertiesToExclude = new HashSet<>(); |
| |
| for (Entry<String,String> entry : options.entrySet()) { |
| if (entry.getKey().startsWith(TableOperationsImpl.CLONE_EXCLUDE_PREFIX)) { |
| propertiesToExclude.add(entry.getKey().substring(TableOperationsImpl.CLONE_EXCLUDE_PREFIX.length())); |
| continue; |
| } |
| |
| if (!TablePropUtil.isPropertyValid(entry.getKey(), entry.getValue())) { |
| throw new ThriftTableOperationException(null, tableName, tableOp, TableOperationExceptionType.OTHER, "Property or value not valid " |
| + entry.getKey() + "=" + entry.getValue()); |
| } |
| |
| propertiesToSet.put(entry.getKey(), entry.getValue()); |
| } |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new CloneTable(c.getPrincipal(), srcTableId, tableName, propertiesToSet, propertiesToExclude)), |
| autoCleanup); |
| |
| break; |
| } |
| case TABLE_DELETE: { |
| TableOperation tableOp = TableOperation.DELETE; |
| String tableName = validateTableNameArgument(arguments.get(0), tableOp, NOT_SYSTEM); |
| |
| final String tableId = ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canDeleteTable; |
| try { |
| canDeleteTable = master.security.canDeleteTable(c, tableId, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, tableName, TableOperation.DELETE); |
| throw e; |
| } |
| |
| if (!canDeleteTable) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| master.fate.seedTransaction(opid, new TraceRepo<>(new DeleteTable(tableId)), autoCleanup); |
| break; |
| } |
| case TABLE_ONLINE: { |
| TableOperation tableOp = TableOperation.ONLINE; |
| final String tableId = validateTableIdArgument(arguments.get(0), tableOp, NOT_ROOT_ID); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canOnlineOfflineTable; |
| try { |
| canOnlineOfflineTable = master.security.canOnlineOfflineTable(c, tableId, op, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, null, TableOperation.ONLINE); |
| throw e; |
| } |
| |
| if (!canOnlineOfflineTable) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new ChangeTableState(tableId, tableOp)), autoCleanup); |
| break; |
| } |
| case TABLE_OFFLINE: { |
| TableOperation tableOp = TableOperation.OFFLINE; |
| final String tableId = validateTableIdArgument(arguments.get(0), tableOp, NOT_ROOT_ID); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canOnlineOfflineTable; |
| try { |
| canOnlineOfflineTable = master.security.canOnlineOfflineTable(c, tableId, op, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, null, TableOperation.OFFLINE); |
| throw e; |
| } |
| |
| if (!canOnlineOfflineTable) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new ChangeTableState(tableId, tableOp)), autoCleanup); |
| break; |
| } |
| case TABLE_MERGE: { |
| TableOperation tableOp = TableOperation.MERGE; |
| String tableName = validateTableNameArgument(arguments.get(0), tableOp, null); |
| Text startRow = ByteBufferUtil.toText(arguments.get(1)); |
| Text endRow = ByteBufferUtil.toText(arguments.get(2)); |
| |
| final String tableId = ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canMerge; |
| try { |
| canMerge = master.security.canMerge(c, tableId, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, tableName, TableOperation.MERGE); |
| throw e; |
| } |
| |
| if (!canMerge) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| Master.log.debug("Creating merge op: " + tableId + " " + startRow + " " + endRow); |
| master.fate.seedTransaction(opid, new TraceRepo<>(new TableRangeOp(MergeInfo.Operation.MERGE, tableId, startRow, endRow)), autoCleanup); |
| break; |
| } |
| case TABLE_DELETE_RANGE: { |
| TableOperation tableOp = TableOperation.DELETE_RANGE; |
| String tableName = validateTableNameArgument(arguments.get(0), tableOp, NOT_METADATA); |
| Text startRow = ByteBufferUtil.toText(arguments.get(1)); |
| Text endRow = ByteBufferUtil.toText(arguments.get(2)); |
| |
| final String tableId = ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canDeleteRange; |
| try { |
| canDeleteRange = master.security.canDeleteRange(c, tableId, tableName, startRow, endRow, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, tableName, TableOperation.DELETE_RANGE); |
| throw e; |
| } |
| |
| if (!canDeleteRange) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new TableRangeOp(MergeInfo.Operation.DELETE, tableId, startRow, endRow)), autoCleanup); |
| break; |
| } |
| case TABLE_BULK_IMPORT: { |
| TableOperation tableOp = TableOperation.BULK_IMPORT; |
| String tableName = validateTableNameArgument(arguments.get(0), tableOp, NOT_SYSTEM); |
| String dir = ByteBufferUtil.toString(arguments.get(1)); |
| String failDir = ByteBufferUtil.toString(arguments.get(2)); |
| boolean setTime = Boolean.parseBoolean(ByteBufferUtil.toString(arguments.get(3))); |
| |
| final String tableId = ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canBulkImport; |
| try { |
| canBulkImport = master.security.canBulkImport(c, tableId, tableName, dir, failDir, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, tableName, TableOperation.BULK_IMPORT); |
| throw e; |
| } |
| |
| if (!canBulkImport) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.updateBulkImportStatus(dir, BulkImportState.INITIAL); |
| master.fate.seedTransaction(opid, new TraceRepo<>(new BulkImport(tableId, dir, failDir, setTime)), autoCleanup); |
| break; |
| } |
| case TABLE_COMPACT: { |
| TableOperation tableOp = TableOperation.COMPACT; |
| String tableId = validateTableIdArgument(arguments.get(0), tableOp, null); |
| byte[] startRow = ByteBufferUtil.toBytes(arguments.get(1)); |
| byte[] endRow = ByteBufferUtil.toBytes(arguments.get(2)); |
| List<IteratorSetting> iterators = IteratorUtil.decodeIteratorSettings(ByteBufferUtil.toBytes(arguments.get(3))); |
| CompactionStrategyConfig compactionStrategy = CompactionStrategyConfigUtil.decode(ByteBufferUtil.toBytes(arguments.get(4))); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canCompact; |
| try { |
| canCompact = master.security.canCompact(c, tableId, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, null, TableOperation.COMPACT); |
| throw e; |
| } |
| |
| if (!canCompact) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new CompactRange(tableId, startRow, endRow, iterators, compactionStrategy)), autoCleanup); |
| break; |
| } |
| case TABLE_CANCEL_COMPACT: { |
| TableOperation tableOp = TableOperation.COMPACT_CANCEL; |
| String tableId = validateTableIdArgument(arguments.get(0), tableOp, null); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canCancelCompact; |
| try { |
| canCancelCompact = master.security.canCompact(c, tableId, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, null, TableOperation.COMPACT_CANCEL); |
| throw e; |
| } |
| |
| if (!canCancelCompact) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new CancelCompactions(tableId)), autoCleanup); |
| break; |
| } |
| case TABLE_IMPORT: { |
| TableOperation tableOp = TableOperation.IMPORT; |
| String tableName = validateTableNameArgument(arguments.get(0), tableOp, NOT_SYSTEM); |
| String exportDir = ByteBufferUtil.toString(arguments.get(1)); |
| String namespaceId; |
| try { |
| namespaceId = Namespaces.getNamespaceId(master.getInstance(), Tables.qualify(tableName).getFirst()); |
| } catch (NamespaceNotFoundException e) { |
| throw new ThriftTableOperationException(null, tableName, tableOp, TableOperationExceptionType.NAMESPACE_NOTFOUND, ""); |
| } |
| |
| final boolean canImport; |
| try { |
| canImport = master.security.canImport(c, tableName, exportDir, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, null, tableName, TableOperation.IMPORT); |
| throw e; |
| } |
| |
| if (!canImport) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new ImportTable(c.getPrincipal(), tableName, exportDir, namespaceId)), autoCleanup); |
| break; |
| } |
| case TABLE_EXPORT: { |
| TableOperation tableOp = TableOperation.EXPORT; |
| String tableName = validateTableNameArgument(arguments.get(0), tableOp, NOT_SYSTEM); |
| String exportDir = ByteBufferUtil.toString(arguments.get(1)); |
| |
| String tableId = ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp); |
| String namespaceId = Tables.getNamespaceId(master.getInstance(), tableId); |
| |
| final boolean canExport; |
| try { |
| canExport = master.security.canExport(c, tableId, tableName, exportDir, namespaceId); |
| } catch (ThriftSecurityException e) { |
| throwIfTableMissingSecurityException(e, tableId, tableName, TableOperation.EXPORT); |
| throw e; |
| } |
| |
| if (!canExport) |
| throw new ThriftSecurityException(c.getPrincipal(), SecurityErrorCode.PERMISSION_DENIED); |
| |
| master.fate.seedTransaction(opid, new TraceRepo<>(new ExportTable(tableName, tableId, exportDir)), autoCleanup); |
| break; |
| } |
| default: |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** |
| * Inspects the {@link ThriftSecurityException} and throws a {@link ThriftTableOperationException} if the {@link SecurityErrorCode} on the |
| * {@link ThriftSecurityException} was {code}TABLE_DOESNT_EXIST{code}. If the {@link ThriftSecurityException} is thrown because a table doesn't exist anymore, |
| * clients will likely see an {@link AccumuloSecurityException} instead of a {@link TableNotFoundException} as expected. If the |
| * {@link ThriftSecurityException} has a different {@link SecurityErrorCode}, this method does nothing and expects the caller to properly handle the original |
| * exception. |
| * |
| * @param e |
| * A caught ThriftSecurityException |
| * @param tableId |
| * Table ID being operated on, or null |
| * @param tableName |
| * Table name being operated on, or null |
| * @param op |
| * The TableOperation the Master was attempting to perform |
| * @throws ThriftTableOperationException |
| * Thrown if {@code e} was thrown because {@link SecurityErrorCode#TABLE_DOESNT_EXIST} |
| */ |
| private void throwIfTableMissingSecurityException(ThriftSecurityException e, String tableId, String tableName, TableOperation op) |
| throws ThriftTableOperationException { |
| // ACCUMULO-3135 Table can be deleted after we get table ID but before we can check permission |
| if (e.isSetCode() && SecurityErrorCode.TABLE_DOESNT_EXIST == e.getCode()) { |
| throw new ThriftTableOperationException(tableId, tableName, op, TableOperationExceptionType.NOTFOUND, "Table no longer exists"); |
| } |
| } |
| |
| @Override |
| public String waitForFateOperation(TInfo tinfo, TCredentials credentials, long opid) throws ThriftSecurityException, ThriftTableOperationException { |
| authenticate(credentials); |
| |
| TStatus status = master.fate.waitForCompletion(opid); |
| if (status == TStatus.FAILED) { |
| Exception e = master.fate.getException(opid); |
| if (e instanceof ThriftTableOperationException) |
| throw (ThriftTableOperationException) e; |
| else if (e instanceof ThriftSecurityException) |
| throw (ThriftSecurityException) e; |
| else if (e instanceof RuntimeException) |
| throw (RuntimeException) e; |
| else |
| throw new RuntimeException(e); |
| } |
| |
| String ret = master.fate.getReturn(opid); |
| if (ret == null) |
| ret = ""; // thrift does not like returning null |
| return ret; |
| } |
| |
| @Override |
| public void finishFateOperation(TInfo tinfo, TCredentials credentials, long opid) throws ThriftSecurityException { |
| authenticate(credentials); |
| master.fate.delete(opid); |
| } |
| |
| protected void authenticate(TCredentials credentials) throws ThriftSecurityException { |
| // this is a bit redundant, the credentials of the caller (the first arg) will throw an exception if it fails to authenticate |
| // before the second arg is checked (which would return true or false) |
| if (!master.security.authenticateUser(credentials, credentials)) |
| throw new ThriftSecurityException(credentials.getPrincipal(), SecurityErrorCode.BAD_CREDENTIALS); |
| } |
| |
| // Verify table name arguments are valid, and match any additional restrictions |
| private String validateTableIdArgument(ByteBuffer tableIdArg, TableOperation op, Validator<String> userValidator) throws ThriftTableOperationException { |
| String tableId = tableIdArg == null ? null : ByteBufferUtil.toString(tableIdArg); |
| try { |
| return VALID_ID.and(userValidator).validate(tableId); |
| } catch (IllegalArgumentException e) { |
| String why = e.getMessage(); |
| // Information provided by a client should generate a user-level exception, not a system-level warning. |
| log.debug(why); |
| throw new ThriftTableOperationException(tableId, null, op, TableOperationExceptionType.INVALID_NAME, why); |
| } |
| } |
| |
| // Verify table name arguments are valid, and match any additional restrictions |
| private String validateTableNameArgument(ByteBuffer tableNameArg, TableOperation op, Validator<String> userValidator) throws ThriftTableOperationException { |
| String tableName = tableNameArg == null ? null : ByteBufferUtil.toString(tableNameArg); |
| return _validateArgument(tableName, op, VALID_NAME.and(userValidator)); |
| } |
| |
| // Verify namespace arguments are valid, and match any additional restrictions |
| private String validateNamespaceArgument(ByteBuffer namespaceArg, TableOperation op, Validator<String> userValidator) throws ThriftTableOperationException { |
| String namespace = namespaceArg == null ? null : ByteBufferUtil.toString(namespaceArg); |
| return _validateArgument(namespace, op, Namespaces.VALID_NAME.and(userValidator)); |
| } |
| |
| // helper to handle the exception |
| private <T> T _validateArgument(T arg, TableOperation op, Validator<T> validator) throws ThriftTableOperationException { |
| try { |
| return validator.validate(arg); |
| } catch (IllegalArgumentException e) { |
| String why = e.getMessage(); |
| // Information provided by a client should generate a user-level exception, not a system-level warning. |
| log.debug(why); |
| throw new ThriftTableOperationException(null, String.valueOf(arg), op, TableOperationExceptionType.INVALID_NAME, why); |
| } |
| } |
| } |