001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.coprocessor;
019
020import static org.junit.Assert.assertEquals;
021
022import com.google.protobuf.ServiceException;
023import java.io.File;
024import java.io.IOException;
025import java.security.PrivilegedExceptionAction;
026import java.util.Arrays;
027import java.util.LinkedList;
028import java.util.List;
029import java.util.Map;
030import java.util.Properties;
031import org.apache.hadoop.conf.Configuration;
032import org.apache.hadoop.fs.FileStatus;
033import org.apache.hadoop.fs.FileSystem;
034import org.apache.hadoop.fs.Path;
035import org.apache.hadoop.fs.permission.FsAction;
036import org.apache.hadoop.fs.permission.FsPermission;
037import org.apache.hadoop.hbase.HBaseClassTestRule;
038import org.apache.hadoop.hbase.HBaseTestingUtility;
039import org.apache.hadoop.hbase.TableName;
040import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
041import org.apache.hadoop.hbase.client.Connection;
042import org.apache.hadoop.hbase.client.ConnectionFactory;
043import org.apache.hadoop.hbase.client.Put;
044import org.apache.hadoop.hbase.client.Result;
045import org.apache.hadoop.hbase.client.ResultScanner;
046import org.apache.hadoop.hbase.client.Scan;
047import org.apache.hadoop.hbase.client.Table;
048import org.apache.hadoop.hbase.client.TableDescriptor;
049import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
050import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
051import org.apache.hadoop.hbase.mapreduce.ExportUtils;
052import org.apache.hadoop.hbase.mapreduce.Import;
053import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos;
054import org.apache.hadoop.hbase.security.HBaseKerberosUtils;
055import org.apache.hadoop.hbase.security.HadoopSecurityEnabledUserProviderForTesting;
056import org.apache.hadoop.hbase.security.User;
057import org.apache.hadoop.hbase.security.UserProvider;
058import org.apache.hadoop.hbase.security.access.AccessControlConstants;
059import org.apache.hadoop.hbase.security.access.AccessControlLists;
060import org.apache.hadoop.hbase.security.access.Permission;
061import org.apache.hadoop.hbase.security.access.SecureTestUtil;
062import org.apache.hadoop.hbase.security.access.SecureTestUtil.AccessTestAction;
063import org.apache.hadoop.hbase.security.visibility.Authorizations;
064import org.apache.hadoop.hbase.security.visibility.CellVisibility;
065import org.apache.hadoop.hbase.security.visibility.VisibilityClient;
066import org.apache.hadoop.hbase.security.visibility.VisibilityConstants;
067import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil;
068import org.apache.hadoop.hbase.testclassification.MediumTests;
069import org.apache.hadoop.hbase.util.Bytes;
070import org.apache.hadoop.hbase.util.Pair;
071import org.apache.hadoop.hdfs.DFSConfigKeys;
072import org.apache.hadoop.http.HttpConfig;
073import org.apache.hadoop.minikdc.MiniKdc;
074import org.apache.hadoop.security.UserGroupInformation;
075import org.apache.hadoop.util.ToolRunner;
076import org.apache.hadoop.yarn.conf.YarnConfiguration;
077import org.junit.After;
078import org.junit.AfterClass;
079import org.junit.Before;
080import org.junit.BeforeClass;
081import org.junit.ClassRule;
082import org.junit.Rule;
083import org.junit.Test;
084import org.junit.experimental.categories.Category;
085import org.junit.rules.TestName;
086import org.slf4j.Logger;
087import org.slf4j.LoggerFactory;
088
089@Category({MediumTests.class})
090public class TestSecureExport {
091  @ClassRule
092  public static final HBaseClassTestRule CLASS_RULE =
093      HBaseClassTestRule.forClass(TestSecureExport.class);
094
095  private static final Logger LOG = LoggerFactory.getLogger(TestSecureExport.class);
096  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
097  private static MiniKdc KDC;
098  private static final File KEYTAB_FILE = new File(UTIL.getDataTestDir("keytab").toUri().getPath());
099  private static String USERNAME;
100  private static String SERVER_PRINCIPAL;
101  private static String HTTP_PRINCIPAL;
102  private static final String FAMILYA_STRING = "fma";
103  private static final String FAMILYB_STRING = "fma";
104  private static final byte[] FAMILYA = Bytes.toBytes(FAMILYA_STRING);
105  private static final byte[] FAMILYB = Bytes.toBytes(FAMILYB_STRING);
106  private static final byte[] ROW1 = Bytes.toBytes("row1");
107  private static final byte[] ROW2 = Bytes.toBytes("row2");
108  private static final byte[] ROW3 = Bytes.toBytes("row3");
109  private static final byte[] QUAL = Bytes.toBytes("qual");
110  private static final String LOCALHOST = "localhost";
111  private static final long NOW = System.currentTimeMillis();
112  // user granted with all global permission
113  private static final String USER_ADMIN = "admin";
114  // user is table owner. will have all permissions on table
115  private static final String USER_OWNER = "owner";
116  // user with rx permissions.
117  private static final String USER_RX = "rxuser";
118  // user with exe-only permissions.
119  private static final String USER_XO = "xouser";
120  // user with read-only permissions.
121  private static final String USER_RO = "rouser";
122  // user with no permissions
123  private static final String USER_NONE = "noneuser";
124  private static final String PRIVATE = "private";
125  private static final String CONFIDENTIAL = "confidential";
126  private static final String SECRET = "secret";
127  private static final String TOPSECRET = "topsecret";
128  @Rule
129  public final TestName name = new TestName();
130  private static void setUpKdcServer() throws Exception {
131    Properties conf = MiniKdc.createConf();
132    conf.put(MiniKdc.DEBUG, true);
133    File kdcFile = new File(UTIL.getDataTestDir("kdc").toUri().getPath());
134    KDC = new MiniKdc(conf, kdcFile);
135    KDC.start();
136    USERNAME = UserGroupInformation.getLoginUser().getShortUserName();
137    SERVER_PRINCIPAL = USERNAME + "/" + LOCALHOST;
138    HTTP_PRINCIPAL = "HTTP/" + LOCALHOST;
139    KDC.createPrincipal(KEYTAB_FILE,
140      SERVER_PRINCIPAL,
141      HTTP_PRINCIPAL,
142      USER_ADMIN + "/" + LOCALHOST,
143      USER_OWNER + "/" + LOCALHOST,
144      USER_RX + "/" + LOCALHOST,
145      USER_RO + "/" + LOCALHOST,
146      USER_XO + "/" + LOCALHOST,
147      USER_NONE + "/" + LOCALHOST);
148  }
149
150  private static User getUserByLogin(final String user) throws IOException {
151    return User.create(UserGroupInformation.loginUserFromKeytabAndReturnUGI(
152        getPrinciple(user), KEYTAB_FILE.getAbsolutePath()));
153  }
154
155  private static String getPrinciple(final String user) {
156    return user + "/" + LOCALHOST + "@" + KDC.getRealm();
157  }
158
159  private static void setUpClusterKdc() throws Exception {
160    HBaseKerberosUtils.setKeytabFileForTesting(KEYTAB_FILE.getAbsolutePath());
161    HBaseKerberosUtils.setPrincipalForTesting(SERVER_PRINCIPAL + "@" + KDC.getRealm());
162    HBaseKerberosUtils.setSecuredConfiguration(UTIL.getConfiguration());
163    // if we drop support for hadoop-2.4.0 and hadoop-2.4.1,
164    // the following key should be changed.
165    // 1) DFS_NAMENODE_USER_NAME_KEY -> DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY
166    // 2) DFS_DATANODE_USER_NAME_KEY -> DFS_DATANODE_KERBEROS_PRINCIPAL_KEY
167    UTIL.getConfiguration().set(DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY,
168        SERVER_PRINCIPAL + "@" + KDC.getRealm());
169    UTIL.getConfiguration().set(DFSConfigKeys.DFS_DATANODE_USER_NAME_KEY,
170        SERVER_PRINCIPAL + "@" + KDC.getRealm());
171    UTIL.getConfiguration().set(DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY,
172        KEYTAB_FILE.getAbsolutePath());
173    UTIL.getConfiguration().set(DFSConfigKeys.DFS_DATANODE_KEYTAB_FILE_KEY,
174        KEYTAB_FILE.getAbsolutePath());
175    // set yarn principal
176    UTIL.getConfiguration().set(YarnConfiguration.RM_PRINCIPAL,
177        SERVER_PRINCIPAL + "@" + KDC.getRealm());
178    UTIL.getConfiguration().set(YarnConfiguration.NM_PRINCIPAL,
179        SERVER_PRINCIPAL + "@" + KDC.getRealm());
180    UTIL.getConfiguration().set(DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY,
181        HTTP_PRINCIPAL + "@" + KDC.getRealm());
182    UTIL.getConfiguration().setBoolean(DFSConfigKeys.DFS_BLOCK_ACCESS_TOKEN_ENABLE_KEY, true);
183    UTIL.getConfiguration().set(DFSConfigKeys.DFS_HTTP_POLICY_KEY,
184        HttpConfig.Policy.HTTPS_ONLY.name());
185    UTIL.getConfiguration().set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY, LOCALHOST + ":0");
186    UTIL.getConfiguration().set(DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY, LOCALHOST + ":0");
187
188    File keystoresDir = new File(UTIL.getDataTestDir("keystore").toUri().getPath());
189    keystoresDir.mkdirs();
190    String sslConfDir = KeyStoreTestUtil.getClasspathDir(TestSecureExport.class);
191    KeyStoreTestUtil.setupSSLConfig(keystoresDir.getAbsolutePath(), sslConfDir,
192        UTIL.getConfiguration(), false);
193
194    UTIL.getConfiguration().setBoolean("ignore.secure.ports.for.testing", true);
195    UserGroupInformation.setConfiguration(UTIL.getConfiguration());
196    UTIL.getConfiguration().set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
197        UTIL.getConfiguration().get(
198            CoprocessorHost.REGION_COPROCESSOR_CONF_KEY) + "," + Export.class.getName());
199  }
200
201  private static void addLabels(final Configuration conf, final List<String> users,
202      final List<String> labels) throws Exception {
203    PrivilegedExceptionAction<VisibilityLabelsProtos.VisibilityLabelsResponse> action
204      = () -> {
205        try (Connection conn = ConnectionFactory.createConnection(conf)) {
206          VisibilityClient.addLabels(conn, labels.toArray(new String[labels.size()]));
207          for (String user : users) {
208            VisibilityClient.setAuths(conn, labels.toArray(new String[labels.size()]), user);
209          }
210        } catch (Throwable t) {
211          throw new IOException(t);
212        }
213        return null;
214      };
215    getUserByLogin(USER_ADMIN).runAs(action);
216  }
217
218  @Before
219  public void announce() {
220    LOG.info("Running " + name.getMethodName());
221  }
222
223  @After
224  public void cleanup() throws IOException {
225  }
226
227  private static void clearOutput(Path path) throws IOException {
228    FileSystem fs = path.getFileSystem(UTIL.getConfiguration());
229    if (fs.exists(path)) {
230      assertEquals(true, fs.delete(path, true));
231    }
232  }
233
234  /**
235   * Sets the security firstly for getting the correct default realm.
236   */
237  @BeforeClass
238  public static void beforeClass() throws Exception {
239    UserProvider.setUserProviderForTesting(UTIL.getConfiguration(),
240        HadoopSecurityEnabledUserProviderForTesting.class);
241    setUpKdcServer();
242    SecureTestUtil.enableSecurity(UTIL.getConfiguration());
243    UTIL.getConfiguration().setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true);
244    VisibilityTestUtil.enableVisiblityLabels(UTIL.getConfiguration());
245    SecureTestUtil.verifyConfiguration(UTIL.getConfiguration());
246    setUpClusterKdc();
247    UTIL.startMiniCluster();
248    UTIL.waitUntilAllRegionsAssigned(AccessControlLists.ACL_TABLE_NAME);
249    UTIL.waitUntilAllRegionsAssigned(VisibilityConstants.LABELS_TABLE_NAME);
250    UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME, 50000);
251    UTIL.waitTableEnabled(VisibilityConstants.LABELS_TABLE_NAME, 50000);
252    SecureTestUtil.grantGlobal(UTIL, USER_ADMIN,
253            Permission.Action.ADMIN,
254            Permission.Action.CREATE,
255            Permission.Action.EXEC,
256            Permission.Action.READ,
257            Permission.Action.WRITE);
258    addLabels(UTIL.getConfiguration(), Arrays.asList(USER_OWNER),
259            Arrays.asList(PRIVATE, CONFIDENTIAL, SECRET, TOPSECRET));
260  }
261
262  @AfterClass
263  public static void afterClass() throws Exception {
264    if (KDC != null) {
265      KDC.stop();
266    }
267    UTIL.shutdownMiniCluster();
268  }
269
270  /**
271   * Test the ExportEndpoint's access levels. The {@link Export} test is ignored
272   * since the access exceptions cannot be collected from the mappers.
273   */
274  @Test
275  public void testAccessCase() throws Throwable {
276    final String exportTable = name.getMethodName();
277    TableDescriptor exportHtd = TableDescriptorBuilder
278            .newBuilder(TableName.valueOf(name.getMethodName()))
279            .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILYA))
280            .setOwnerString(USER_OWNER)
281            .build();
282    SecureTestUtil.createTable(UTIL, exportHtd, new byte[][]{Bytes.toBytes("s")});
283    SecureTestUtil.grantOnTable(UTIL, USER_RO,
284            TableName.valueOf(exportTable), null, null,
285            Permission.Action.READ);
286    SecureTestUtil.grantOnTable(UTIL, USER_RX,
287            TableName.valueOf(exportTable), null, null,
288            Permission.Action.READ,
289            Permission.Action.EXEC);
290    SecureTestUtil.grantOnTable(UTIL, USER_XO,
291            TableName.valueOf(exportTable), null, null,
292            Permission.Action.EXEC);
293    assertEquals(4, AccessControlLists.getTablePermissions(UTIL.getConfiguration(),
294            TableName.valueOf(exportTable)).size());
295    AccessTestAction putAction = () -> {
296      Put p = new Put(ROW1);
297      p.addColumn(FAMILYA, Bytes.toBytes("qual_0"), NOW, QUAL);
298      p.addColumn(FAMILYA, Bytes.toBytes("qual_1"), NOW, QUAL);
299      try (Connection conn = ConnectionFactory.createConnection(UTIL.getConfiguration());
300              Table t = conn.getTable(TableName.valueOf(exportTable))) {
301        t.put(p);
302      }
303      return null;
304    };
305    // no hdfs access.
306    SecureTestUtil.verifyAllowed(putAction,
307      getUserByLogin(USER_ADMIN),
308      getUserByLogin(USER_OWNER));
309    SecureTestUtil.verifyDenied(putAction,
310      getUserByLogin(USER_RO),
311      getUserByLogin(USER_XO),
312      getUserByLogin(USER_RX),
313      getUserByLogin(USER_NONE));
314
315    final FileSystem fs = UTIL.getDFSCluster().getFileSystem();
316    final Path openDir = fs.makeQualified(new Path("testAccessCase"));
317    fs.mkdirs(openDir);
318    fs.setPermission(openDir, new FsPermission(FsAction.ALL, FsAction.ALL, FsAction.ALL));
319    final Path output = fs.makeQualified(new Path(openDir, "output"));
320    AccessTestAction exportAction = () -> {
321      try {
322        String[] args = new String[]{exportTable, output.toString()};
323        Map<byte[], Export.Response> result
324                = Export.run(new Configuration(UTIL.getConfiguration()), args);
325        long rowCount = 0;
326        long cellCount = 0;
327        for (Export.Response r : result.values()) {
328          rowCount += r.getRowCount();
329          cellCount += r.getCellCount();
330        }
331        assertEquals(1, rowCount);
332        assertEquals(2, cellCount);
333        return null;
334      } catch (ServiceException | IOException ex) {
335        throw ex;
336      } catch (Throwable ex) {
337        LOG.error(ex.toString(), ex);
338        throw new Exception(ex);
339      } finally {
340        if (fs.exists(new Path(openDir, "output"))) {
341          // if export completes successfully, every file under the output directory should be
342          // owned by the current user, not the hbase service user.
343          FileStatus outputDirFileStatus = fs.getFileStatus(new Path(openDir, "output"));
344          String currentUserName = User.getCurrent().getShortName();
345          assertEquals("Unexpected file owner", currentUserName, outputDirFileStatus.getOwner());
346
347          FileStatus[] outputFileStatus = fs.listStatus(new Path(openDir, "output"));
348          for (FileStatus fileStatus: outputFileStatus) {
349            assertEquals("Unexpected file owner", currentUserName, fileStatus.getOwner());
350          }
351        } else {
352          LOG.info("output directory doesn't exist. Skip check");
353        }
354
355        clearOutput(output);
356      }
357    };
358    SecureTestUtil.verifyDenied(exportAction,
359      getUserByLogin(USER_RO),
360      getUserByLogin(USER_XO),
361      getUserByLogin(USER_NONE));
362    SecureTestUtil.verifyAllowed(exportAction,
363      getUserByLogin(USER_ADMIN),
364      getUserByLogin(USER_OWNER),
365      getUserByLogin(USER_RX));
366    AccessTestAction deleteAction = () -> {
367      UTIL.deleteTable(TableName.valueOf(exportTable));
368      return null;
369    };
370    SecureTestUtil.verifyAllowed(deleteAction, getUserByLogin(USER_OWNER));
371    fs.delete(openDir, true);
372  }
373
374  @Test
375  public void testVisibilityLabels() throws IOException, Throwable {
376    final String exportTable = name.getMethodName() + "_export";
377    final String importTable = name.getMethodName() + "_import";
378    final TableDescriptor exportHtd = TableDescriptorBuilder
379            .newBuilder(TableName.valueOf(exportTable))
380            .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILYA))
381            .setOwnerString(USER_OWNER)
382            .build();
383    SecureTestUtil.createTable(UTIL, exportHtd, new byte[][]{Bytes.toBytes("s")});
384    AccessTestAction putAction = () -> {
385      Put p1 = new Put(ROW1);
386      p1.addColumn(FAMILYA, QUAL, NOW, QUAL);
387      p1.setCellVisibility(new CellVisibility(SECRET));
388      Put p2 = new Put(ROW2);
389      p2.addColumn(FAMILYA, QUAL, NOW, QUAL);
390      p2.setCellVisibility(new CellVisibility(PRIVATE + " & " + CONFIDENTIAL));
391      Put p3 = new Put(ROW3);
392      p3.addColumn(FAMILYA, QUAL, NOW, QUAL);
393      p3.setCellVisibility(new CellVisibility("!" + CONFIDENTIAL + " & " + TOPSECRET));
394      try (Connection conn = ConnectionFactory.createConnection(UTIL.getConfiguration());
395              Table t = conn.getTable(TableName.valueOf(exportTable))) {
396        t.put(p1);
397        t.put(p2);
398        t.put(p3);
399      }
400      return null;
401    };
402    SecureTestUtil.verifyAllowed(putAction, getUserByLogin(USER_OWNER));
403    List<Pair<List<String>, Integer>> labelsAndRowCounts = new LinkedList<>();
404    labelsAndRowCounts.add(new Pair<>(Arrays.asList(SECRET), 1));
405    labelsAndRowCounts.add(new Pair<>(Arrays.asList(PRIVATE, CONFIDENTIAL), 1));
406    labelsAndRowCounts.add(new Pair<>(Arrays.asList(TOPSECRET), 1));
407    labelsAndRowCounts.add(new Pair<>(Arrays.asList(TOPSECRET, CONFIDENTIAL), 0));
408    labelsAndRowCounts.add(new Pair<>(Arrays.asList(TOPSECRET, CONFIDENTIAL, PRIVATE, SECRET), 2));
409    for (final Pair<List<String>, Integer> labelsAndRowCount : labelsAndRowCounts) {
410      final List<String> labels = labelsAndRowCount.getFirst();
411      final int rowCount = labelsAndRowCount.getSecond();
412      //create a open permission directory.
413      final Path openDir = new Path("testAccessCase");
414      final FileSystem fs = openDir.getFileSystem(UTIL.getConfiguration());
415      fs.mkdirs(openDir);
416      fs.setPermission(openDir, new FsPermission(FsAction.ALL, FsAction.ALL, FsAction.ALL));
417      final Path output = fs.makeQualified(new Path(openDir, "output"));
418      AccessTestAction exportAction = () -> {
419        StringBuilder buf = new StringBuilder();
420        labels.forEach(v -> buf.append(v).append(","));
421        buf.deleteCharAt(buf.length() - 1);
422        try {
423          String[] args = new String[]{
424            "-D " + ExportUtils.EXPORT_VISIBILITY_LABELS + "=" + buf.toString(),
425            exportTable,
426            output.toString(),};
427          Export.run(new Configuration(UTIL.getConfiguration()), args);
428          return null;
429        } catch (ServiceException | IOException ex) {
430          throw ex;
431        } catch (Throwable ex) {
432          throw new Exception(ex);
433        }
434      };
435      SecureTestUtil.verifyAllowed(exportAction, getUserByLogin(USER_OWNER));
436      final TableDescriptor importHtd = TableDescriptorBuilder
437              .newBuilder(TableName.valueOf(importTable))
438              .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILYB))
439              .setOwnerString(USER_OWNER)
440              .build();
441      SecureTestUtil.createTable(UTIL, importHtd, new byte[][]{Bytes.toBytes("s")});
442      AccessTestAction importAction = () -> {
443        String[] args = new String[]{
444          "-D" + Import.CF_RENAME_PROP + "=" + FAMILYA_STRING + ":" + FAMILYB_STRING,
445          importTable,
446          output.toString()
447        };
448        assertEquals(0, ToolRunner.run(
449            new Configuration(UTIL.getConfiguration()), new Import(), args));
450        return null;
451      };
452      SecureTestUtil.verifyAllowed(importAction, getUserByLogin(USER_OWNER));
453      AccessTestAction scanAction = () -> {
454        Scan scan = new Scan();
455        scan.setAuthorizations(new Authorizations(labels));
456        try (Connection conn = ConnectionFactory.createConnection(UTIL.getConfiguration());
457                Table table = conn.getTable(importHtd.getTableName());
458                ResultScanner scanner = table.getScanner(scan)) {
459          int count = 0;
460          for (Result r : scanner) {
461            ++count;
462          }
463          assertEquals(rowCount, count);
464        }
465        return null;
466      };
467      SecureTestUtil.verifyAllowed(scanAction, getUserByLogin(USER_OWNER));
468      AccessTestAction deleteAction = () -> {
469        UTIL.deleteTable(importHtd.getTableName());
470        return null;
471      };
472      SecureTestUtil.verifyAllowed(deleteAction, getUserByLogin(USER_OWNER));
473      clearOutput(output);
474    }
475    AccessTestAction deleteAction = () -> {
476      UTIL.deleteTable(exportHtd.getTableName());
477      return null;
478    };
479    SecureTestUtil.verifyAllowed(deleteAction, getUserByLogin(USER_OWNER));
480  }
481}