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}