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.security.access;
019
020import static org.apache.hadoop.hbase.security.access.Permission.Action.READ;
021import static org.apache.hadoop.hbase.security.access.Permission.Action.WRITE;
022import static org.apache.hadoop.hbase.security.access.SnapshotScannerHDFSAclController.SnapshotScannerHDFSAclStorage.hasUserGlobalHdfsAcl;
023import static org.apache.hadoop.hbase.security.access.SnapshotScannerHDFSAclController.SnapshotScannerHDFSAclStorage.hasUserNamespaceHdfsAcl;
024import static org.apache.hadoop.hbase.security.access.SnapshotScannerHDFSAclController.SnapshotScannerHDFSAclStorage.hasUserTableHdfsAcl;
025import static org.junit.jupiter.api.Assertions.assertEquals;
026import static org.junit.jupiter.api.Assertions.assertFalse;
027import static org.junit.jupiter.api.Assertions.assertTrue;
028
029import java.io.IOException;
030import java.util.List;
031import org.apache.hadoop.conf.Configuration;
032import org.apache.hadoop.fs.FileSystem;
033import org.apache.hadoop.fs.Path;
034import org.apache.hadoop.fs.permission.AclEntry;
035import org.apache.hadoop.fs.permission.AclEntryScope;
036import org.apache.hadoop.fs.permission.FsPermission;
037import org.apache.hadoop.hbase.HBaseTestingUtil;
038import org.apache.hadoop.hbase.NamespaceDescriptor;
039import org.apache.hadoop.hbase.TableName;
040import org.apache.hadoop.hbase.client.Admin;
041import org.apache.hadoop.hbase.client.Table;
042import org.apache.hadoop.hbase.client.TableDescriptor;
043import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
044import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
045import org.apache.hadoop.hbase.master.cleaner.HFileCleaner;
046import org.apache.hadoop.hbase.security.User;
047import org.apache.hadoop.hbase.testclassification.LargeTests;
048import org.apache.hadoop.hbase.testclassification.SecurityTests;
049import org.apache.hadoop.hbase.util.FSUtils;
050import org.apache.hadoop.hbase.util.HFileArchiveUtil;
051import org.apache.hadoop.hbase.util.Threads;
052import org.junit.jupiter.api.AfterAll;
053import org.junit.jupiter.api.BeforeAll;
054import org.junit.jupiter.api.Tag;
055import org.junit.jupiter.api.Test;
056import org.junit.jupiter.api.TestInfo;
057import org.slf4j.Logger;
058import org.slf4j.LoggerFactory;
059
060@Tag(SecurityTests.TAG)
061@Tag(LargeTests.TAG)
062public class TestSnapshotScannerHDFSAclController {
063  private static final Logger LOG =
064    LoggerFactory.getLogger(TestSnapshotScannerHDFSAclController.class);
065
066  private static final String UN_GRANT_USER = "un_grant_user";
067  private static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
068  private static Configuration conf = TEST_UTIL.getConfiguration();
069  private static Admin admin = null;
070  private static FileSystem FS = null;
071  private static Path rootDir = null;
072  private static User unGrantUser = null;
073  private static SnapshotScannerHDFSAclHelper helper;
074  private static Table aclTable;
075
076  @BeforeAll
077  public static void setupBeforeClass() throws Exception {
078    // enable hdfs acl and set umask to 027
079    conf.setBoolean("dfs.namenode.acls.enabled", true);
080    conf.set("fs.permissions.umask-mode", "027");
081    // enable hbase hdfs acl feature
082    conf.setBoolean(SnapshotScannerHDFSAclHelper.ACL_SYNC_TO_HDFS_ENABLE, true);
083    // enable secure
084    conf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
085    conf.set(SnapshotScannerHDFSAclHelper.SNAPSHOT_RESTORE_TMP_DIR,
086      SnapshotScannerHDFSAclHelper.SNAPSHOT_RESTORE_TMP_DIR_DEFAULT);
087    SecureTestUtil.enableSecurity(conf);
088    // add SnapshotScannerHDFSAclController coprocessor
089    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
090      conf.get(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY) + ","
091        + SnapshotScannerHDFSAclController.class.getName());
092
093    TEST_UTIL.startMiniCluster();
094    SnapshotScannerHDFSAclController coprocessor = TEST_UTIL.getHBaseCluster().getMaster()
095      .getMasterCoprocessorHost().findCoprocessor(SnapshotScannerHDFSAclController.class);
096    TEST_UTIL.waitFor(30000, () -> coprocessor.checkInitialized("check initialized"));
097    TEST_UTIL.waitTableAvailable(PermissionStorage.ACL_TABLE_NAME);
098
099    admin = TEST_UTIL.getAdmin();
100    rootDir = TEST_UTIL.getDefaultRootDirPath();
101    FS = rootDir.getFileSystem(conf);
102    unGrantUser = User.createUserForTesting(conf, UN_GRANT_USER, new String[] {});
103    helper = new SnapshotScannerHDFSAclHelper(conf, admin.getConnection());
104
105    // set hbase directory permission
106    FsPermission commonDirectoryPermission =
107      new FsPermission(conf.get(SnapshotScannerHDFSAclHelper.COMMON_DIRECTORY_PERMISSION,
108        SnapshotScannerHDFSAclHelper.COMMON_DIRECTORY_PERMISSION_DEFAULT));
109    Path path = rootDir;
110    while (path != null) {
111      FS.setPermission(path, commonDirectoryPermission);
112      path = path.getParent();
113    }
114    // set restore directory permission
115    Path restoreDir = new Path(SnapshotScannerHDFSAclHelper.SNAPSHOT_RESTORE_TMP_DIR_DEFAULT);
116    if (!FS.exists(restoreDir)) {
117      FS.mkdirs(restoreDir);
118      FS.setPermission(restoreDir,
119        new FsPermission(
120          conf.get(SnapshotScannerHDFSAclHelper.SNAPSHOT_RESTORE_DIRECTORY_PERMISSION,
121            SnapshotScannerHDFSAclHelper.SNAPSHOT_RESTORE_DIRECTORY_PERMISSION_DEFAULT)));
122    }
123    path = restoreDir.getParent();
124    while (path != null) {
125      FS.setPermission(path, commonDirectoryPermission);
126      path = path.getParent();
127    }
128    aclTable = admin.getConnection().getTable(PermissionStorage.ACL_TABLE_NAME);
129  }
130
131  @AfterAll
132  public static void tearDownAfterClass() throws Exception {
133    TEST_UTIL.shutdownMiniCluster();
134  }
135
136  private void snapshotAndWait(final String snapShotName, final TableName tableName)
137    throws Exception {
138    admin.snapshot(snapShotName, tableName);
139    LOG.info("Sleep for three seconds, waiting for HDFS Acl setup");
140    Threads.sleep(3000);
141  }
142
143  @Test
144  public void testGrantGlobal1(TestInfo testInfo) throws Exception {
145    final String grantUserName = testInfo.getTestMethod().get().getName();
146    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
147    String namespace = testInfo.getTestMethod().get().getName();
148    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
149    String snapshot1 = namespace + "s1";
150    String snapshot2 = namespace + "s2";
151
152    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
153    snapshotAndWait(snapshot1, table);
154    // grant G(R)
155    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
156    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
157    assertTrue(hasUserGlobalHdfsAcl(aclTable, grantUserName));
158    // grant G(W) with merging existing permissions
159    admin.grant(
160      new UserPermission(grantUserName, Permission.newBuilder().withActions(WRITE).build()), true);
161    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
162    assertTrue(hasUserGlobalHdfsAcl(aclTable, grantUserName));
163    // grant G(W) without merging
164    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, WRITE);
165    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, -1);
166    assertFalse(hasUserGlobalHdfsAcl(aclTable, grantUserName));
167    // grant G(R)
168    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
169    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
170    // take a snapshot and ACLs are inherited automatically
171    snapshotAndWait(snapshot2, table);
172    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot2, 6);
173    assertTrue(hasUserGlobalHdfsAcl(aclTable, grantUserName));
174    deleteTable(table);
175  }
176
177  @Test
178  public void testGrantGlobal2(TestInfo testInfo) throws Exception {
179    final String grantUserName = testInfo.getTestMethod().get().getName();
180    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
181    String namespace1 = testInfo.getTestMethod().get().getName();
182    TableName table1 =
183      TableName.valueOf(namespace1, testInfo.getTestMethod().get().getName() + ".1");
184    String namespace2 = namespace1 + "2";
185    TableName table2 =
186      TableName.valueOf(namespace2, testInfo.getTestMethod().get().getName() + ".2");
187    String snapshot1 = namespace1 + "s1";
188    String snapshot2 = namespace2 + "s2";
189
190    // grant G(R), grant namespace1(R)
191    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
192    // create table in namespace1 and snapshot
193    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
194    snapshotAndWait(snapshot1, table1);
195    admin.grant(new UserPermission(grantUserName,
196      Permission.newBuilder(namespace1).withActions(READ).build()), false);
197    // grant G(W)
198    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, WRITE);
199    // create table in namespace2 and snapshot
200    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table2);
201    snapshotAndWait(snapshot2, table2);
202    // check scan snapshot
203    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
204    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot2, -1);
205    assertFalse(hasUserGlobalHdfsAcl(aclTable, grantUserName));
206    assertTrue(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace1));
207    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace2));
208    checkUserAclEntry(FS, helper.getGlobalRootPaths(), grantUserName, false, false);
209    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace1), grantUserName, true, true);
210    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace2), grantUserName, false, false);
211    deleteTable(table1);
212    deleteTable(table2);
213  }
214
215  @Test
216  public void testGrantGlobal3(TestInfo testInfo) throws Exception {
217    final String grantUserName = testInfo.getTestMethod().get().getName();
218    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
219    String namespace = testInfo.getTestMethod().get().getName();
220    TableName table1 =
221      TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".1");
222    TableName table2 =
223      TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".2");
224    String snapshot1 = namespace + "s1";
225    String snapshot2 = namespace + "s2";
226    // grant G(R)
227    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
228    // grant table1(R)
229    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
230    snapshotAndWait(snapshot1, table1);
231    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table1, READ);
232    // grant G(W)
233    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, WRITE);
234    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table2);
235    snapshotAndWait(snapshot2, table2);
236    // check scan snapshot
237    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
238    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot2, -1);
239    assertFalse(hasUserGlobalHdfsAcl(aclTable, grantUserName));
240    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
241    assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, table1));
242    assertFalse(hasUserTableHdfsAcl(aclTable, grantUserName, table2));
243    checkUserAclEntry(FS, helper.getGlobalRootPaths(), grantUserName, false, false);
244    checkUserAclEntry(FS, helper.getTableRootPaths(table2, false), grantUserName, false, false);
245    checkUserAclEntry(FS, helper.getTableRootPaths(table1, false), grantUserName, true, true);
246    deleteTable(table1);
247    deleteTable(table2);
248  }
249
250  @Test
251  public void testGrantNamespace1(TestInfo testInfo) throws Exception {
252    final String grantUserName = testInfo.getTestMethod().get().getName();
253    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
254    String namespace = testInfo.getTestMethod().get().getName();
255    TableName table1 =
256      TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".1");
257    TableName table2 =
258      TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".2");
259    String snapshot1 = namespace + "s1";
260    String snapshot2 = namespace + "s2";
261
262    // create table1 and snapshot
263    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
264    snapshotAndWait(snapshot1, table1);
265    // grant N(R)
266    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
267    // create table2 and snapshot, ACLs can be inherited automatically
268    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table2);
269    snapshotAndWait(snapshot2, table2);
270    // check scan snapshot
271    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
272    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot2, 6);
273    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, unGrantUser, snapshot1, -1);
274    assertTrue(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
275    assertFalse(hasUserTableHdfsAcl(aclTable, grantUserName, table1));
276    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, true);
277    // grant N(W)
278    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, WRITE);
279    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, -1);
280    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
281    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, false, false);
282    deleteTable(table1);
283    deleteTable(table2);
284  }
285
286  @Test
287  public void testGrantNamespace2(TestInfo testInfo) throws Exception {
288    final String grantUserName = testInfo.getTestMethod().get().getName();
289    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
290    String namespace = testInfo.getTestMethod().get().getName();
291    TableName table1 = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
292    String snapshot1 = namespace + "s1";
293
294    // create table1 and snapshot
295    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
296    snapshotAndWait(snapshot1, table1);
297
298    // grant N(R)
299    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
300    // grant table1(R)
301    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table1, READ);
302    // grant N(W)
303    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, WRITE);
304    // check scan snapshot
305    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
306    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
307    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, false);
308    assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, table1));
309    checkUserAclEntry(FS, helper.getTableRootPaths(table1, false), grantUserName, true, true);
310    deleteTable(table1);
311  }
312
313  @Test
314  public void testGrantNamespace3(TestInfo testInfo) throws Exception {
315    final String grantUserName = testInfo.getTestMethod().get().getName();
316    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
317    String namespace = testInfo.getTestMethod().get().getName();
318    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
319    String snapshot = namespace + "t1";
320
321    // create table1 and snapshot
322    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
323    snapshotAndWait(snapshot, table);
324    // grant namespace(R)
325    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
326    // grant global(R)
327    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
328    // grant namespace(W)
329    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, WRITE);
330    // check scan snapshot
331    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
332    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
333    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, true);
334    assertTrue(hasUserGlobalHdfsAcl(aclTable, grantUserName));
335    checkUserAclEntry(FS, helper.getGlobalRootPaths(), grantUserName, true, true);
336    deleteTable(table);
337  }
338
339  @Test
340  public void testGrantTable(TestInfo testInfo) throws Exception {
341    final String grantUserName = testInfo.getTestMethod().get().getName();
342    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
343
344    String namespace = testInfo.getTestMethod().get().getName();
345    TableName table1 = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
346    String snapshot1 = namespace + "s1";
347    String snapshot2 = namespace + "s2";
348
349    LOG.info("Create table");
350    try (Table t = TestHDFSAclHelper.createTable(TEST_UTIL, table1)) {
351      TestHDFSAclHelper.put(t);
352      snapshotAndWait(snapshot1, table1);
353      // table owner can scan table snapshot
354      LOG.info("Scan snapshot");
355      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL,
356        User.createUserForTesting(conf, "owner", new String[] {}), snapshot1, 6);
357      // grant table1 family(R)
358      SecureTestUtil.grantOnTable(TEST_UTIL, grantUserName, table1, TestHDFSAclHelper.COLUMN1, null,
359        READ);
360      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, -1);
361
362      // grant table1(R)
363      TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table1, READ);
364      TestHDFSAclHelper.put2(t);
365      snapshotAndWait(snapshot2, table1);
366
367      // check scan snapshot
368      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
369      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot2, 10);
370      assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, table1));
371      checkUserAclEntry(FS, helper.getTableRootPaths(table1, false), grantUserName, true, true);
372    }
373
374    // grant table1(W) with merging existing permissions
375    admin.grant(
376      new UserPermission(grantUserName, Permission.newBuilder(table1).withActions(WRITE).build()),
377      true);
378    assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, table1));
379    checkUserAclEntry(FS, helper.getTableRootPaths(table1, false), grantUserName, true, true);
380
381    // grant table1(W) without merging existing permissions
382    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table1, WRITE);
383    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, -1);
384    assertFalse(hasUserTableHdfsAcl(aclTable, grantUserName, table1));
385    checkUserAclEntry(FS, helper.getTableRootPaths(table1, false), grantUserName, false, false);
386    deleteTable(table1);
387  }
388
389  @Test
390  public void testGrantMobTable(TestInfo testInfo) throws Exception {
391    final String grantUserName = testInfo.getTestMethod().get().getName();
392    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
393    String namespace = testInfo.getTestMethod().get().getName();
394    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
395    String snapshot = namespace + "s1";
396
397    try (Table t = TestHDFSAclHelper.createMobTable(TEST_UTIL, table)) {
398      TestHDFSAclHelper.put(t);
399      snapshotAndWait(snapshot, table);
400      TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table, READ);
401      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
402      assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, table));
403      checkUserAclEntry(FS, helper.getTableRootPaths(table, false), grantUserName, true, true);
404    }
405    deleteTable(table);
406  }
407
408  @Test
409  public void testRevokeGlobal1(TestInfo testInfo) throws Exception {
410    final String grantUserName = testInfo.getTestMethod().get().getName();
411    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
412    String namespace = testInfo.getTestMethod().get().getName();
413    TableName table1 = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
414    String snapshot1 = namespace + "t1";
415
416    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
417    snapshotAndWait(snapshot1, table1);
418    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
419    SecureTestUtil.revokeGlobal(TEST_UTIL, grantUserName, READ);
420    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, -1);
421    assertFalse(hasUserGlobalHdfsAcl(aclTable, grantUserName));
422    checkUserAclEntry(FS, helper.getGlobalRootPaths(), grantUserName, false, false);
423    deleteTable(table1);
424  }
425
426  @Test
427  public void testRevokeGlobal2(TestInfo testInfo) throws Exception {
428    final String grantUserName = testInfo.getTestMethod().get().getName();
429    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
430
431    String namespace = testInfo.getTestMethod().get().getName();
432    String snapshot1 = namespace + "s1";
433    TableName table1 = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
434    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
435    snapshotAndWait(snapshot1, table1);
436
437    // grant G(R), grant N(R), grant T(R) -> revoke G(R)
438    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
439    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
440    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table1, READ);
441    SecureTestUtil.revokeGlobal(TEST_UTIL, grantUserName, READ);
442    // check scan snapshot
443    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
444    assertFalse(hasUserGlobalHdfsAcl(aclTable, grantUserName));
445    checkUserAclEntry(FS, helper.getGlobalRootPaths(), grantUserName, false, false);
446    assertTrue(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
447    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, true);
448    deleteTable(table1);
449  }
450
451  @Test
452  public void testRevokeGlobal3(TestInfo testInfo) throws Exception {
453    final String grantUserName = testInfo.getTestMethod().get().getName();
454    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
455
456    String namespace = testInfo.getTestMethod().get().getName();
457    TableName table1 = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
458    String snapshot1 = namespace + "t1";
459    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
460    snapshotAndWait(snapshot1, table1);
461
462    // grant G(R), grant T(R) -> revoke G(R)
463    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
464    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table1, READ);
465    SecureTestUtil.revokeGlobal(TEST_UTIL, grantUserName, READ);
466    // check scan snapshot
467    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
468    assertFalse(hasUserGlobalHdfsAcl(aclTable, grantUserName));
469    checkUserAclEntry(FS, helper.getGlobalRootPaths(), grantUserName, false, false);
470    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
471    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, false);
472    assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, table1));
473    checkUserAclEntry(FS, helper.getTableRootPaths(table1, false), grantUserName, true, true);
474    deleteTable(table1);
475  }
476
477  @Test
478  public void testRevokeNamespace1(TestInfo testInfo) throws Exception {
479    String grantUserName = testInfo.getTestMethod().get().getName();
480    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
481    String namespace = testInfo.getTestMethod().get().getName();
482    TableName table1 = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
483    String snapshot1 = namespace + "s1";
484    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table1);
485    snapshotAndWait(snapshot1, table1);
486
487    // revoke N(R)
488    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
489    admin.revoke(new UserPermission(grantUserName, Permission.newBuilder(namespace).build()));
490    // check scan snapshot
491    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, -1);
492    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
493    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, false, false);
494
495    // grant N(R), grant G(R) -> revoke N(R)
496    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
497    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
498    admin.revoke(new UserPermission(grantUserName, Permission.newBuilder(namespace).build()));
499    // check scan snapshot
500    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot1, 6);
501    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
502    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, true);
503    deleteTable(table1);
504  }
505
506  @Test
507  public void testRevokeNamespace2(TestInfo testInfo) throws Exception {
508    String grantUserName = testInfo.getTestMethod().get().getName();
509    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
510    String namespace = testInfo.getTestMethod().get().getName();
511    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
512    String snapshot = namespace + "s1";
513    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
514    snapshotAndWait(snapshot, table);
515
516    // grant N(R), grant T(R) -> revoke N(R)
517    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
518    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table, READ);
519    SecureTestUtil.revokeFromNamespace(TEST_UTIL, grantUserName, namespace, READ);
520    // check scan snapshot
521    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
522    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
523    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, false);
524    assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, table));
525    checkUserAclEntry(FS, helper.getTableRootPaths(table, false), grantUserName, true, true);
526    deleteTable(table);
527  }
528
529  @Test
530  public void testRevokeTable1(TestInfo testInfo) throws Exception {
531    final String grantUserName = testInfo.getTestMethod().get().getName();
532    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
533    String namespace = testInfo.getTestMethod().get().getName();
534    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
535    String snapshot = namespace + "t1";
536    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
537    snapshotAndWait(snapshot, table);
538
539    // grant T(R) -> revoke table family
540    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table, READ);
541    SecureTestUtil.revokeFromTable(TEST_UTIL, grantUserName, table, TestHDFSAclHelper.COLUMN1, null,
542      READ);
543    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
544
545    // grant T(R) -> revoke T(R)
546    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table, READ);
547    admin.revoke(new UserPermission(grantUserName, Permission.newBuilder(table).build()));
548    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, -1);
549    assertFalse(hasUserTableHdfsAcl(aclTable, grantUserName, table));
550    checkUserAclEntry(FS, helper.getTableRootPaths(table, false), grantUserName, false, false);
551    deleteTable(table);
552  }
553
554  @Test
555  public void testRevokeTable2(TestInfo testInfo) throws Exception {
556    final String grantUserName = testInfo.getTestMethod().get().getName();
557    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
558    String namespace = testInfo.getTestMethod().get().getName();
559    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
560    String snapshot = namespace + "t1";
561    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
562    snapshotAndWait(snapshot, table);
563
564    // grant T(R), grant N(R) -> revoke T(R)
565    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table, READ);
566    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
567    admin.revoke(new UserPermission(grantUserName, Permission.newBuilder(table).build()));
568    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
569    assertFalse(hasUserTableHdfsAcl(aclTable, grantUserName, table));
570    checkUserAclEntry(FS, helper.getTableRootPaths(table, false), grantUserName, true, true);
571    assertTrue(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
572    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, true);
573    deleteTable(table);
574  }
575
576  @Test
577  public void testRevokeTable3(TestInfo testInfo) throws Exception {
578    final String grantUserName = testInfo.getTestMethod().get().getName();
579    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
580    String namespace = testInfo.getTestMethod().get().getName();
581    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
582    String snapshot = namespace + "t1";
583    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
584    snapshotAndWait(snapshot, table);
585
586    // grant T(R), grant G(R) -> revoke T(R)
587    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table, READ);
588    SecureTestUtil.grantGlobal(TEST_UTIL, grantUserName, READ);
589    admin.revoke(new UserPermission(grantUserName, Permission.newBuilder(table).build()));
590    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
591    assertFalse(hasUserTableHdfsAcl(aclTable, grantUserName, table));
592    checkUserAclEntry(FS, helper.getTableRootPaths(table, false), grantUserName, true, true);
593    assertTrue(hasUserGlobalHdfsAcl(aclTable, grantUserName));
594    checkUserAclEntry(FS, helper.getGlobalRootPaths(), grantUserName, true, true);
595    deleteTable(table);
596  }
597
598  @Test
599  public void testTruncateTable(TestInfo testInfo) throws Exception {
600    String grantUserName = testInfo.getTestMethod().get().getName();
601    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
602    String grantUserName2 = grantUserName + "2";
603    User grantUser2 = User.createUserForTesting(conf, grantUserName2, new String[] {});
604
605    String namespace = testInfo.getTestMethod().get().getName();
606    TableName tableName = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
607    String snapshot = namespace + "s1";
608    String snapshot2 = namespace + "s2";
609    try (Table t = TestHDFSAclHelper.createTable(TEST_UTIL, tableName)) {
610      TestHDFSAclHelper.put(t);
611      // snapshot
612      snapshotAndWait(snapshot, tableName);
613      // grant user2 namespace permission
614      SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName2, namespace, READ);
615      // grant user table permission
616      TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, tableName, READ);
617      // truncate table
618      admin.disableTable(tableName);
619      admin.truncateTable(tableName, true);
620      TestHDFSAclHelper.put2(t);
621      // snapshot
622      snapshotAndWait(snapshot2, tableName);
623      // check scan snapshot
624      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
625      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser2, snapshot, 6);
626      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot2, 9);
627      TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser2, snapshot2, 9);
628      assertTrue(hasUserNamespaceHdfsAcl(aclTable, grantUserName2, namespace));
629      checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName2, true, true);
630      assertTrue(hasUserTableHdfsAcl(aclTable, grantUserName, tableName));
631      checkUserAclEntry(FS, helper.getTableRootPaths(tableName, false), grantUserName, true, true);
632      checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName, true, false);
633    }
634    deleteTable(tableName);
635  }
636
637  @Test
638  public void testDeleteTable(TestInfo testInfo) throws Exception {
639    String namespace = testInfo.getTestMethod().get().getName();
640    String grantUserName1 = namespace + "1";
641    String grantUserName2 = namespace + "2";
642    User grantUser1 = User.createUserForTesting(conf, grantUserName1, new String[] {});
643    User grantUser2 = User.createUserForTesting(conf, grantUserName2, new String[] {});
644    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
645    String snapshot1 = namespace + "t1";
646
647    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
648    // snapshot
649    snapshotAndWait(snapshot1, table);
650    // grant user table permission
651    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName1, table, READ);
652    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName2, namespace, READ);
653    // delete table
654    admin.disableTable(table);
655    admin.deleteTable(table);
656    // grantUser2 should have data/ns acl
657    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser1, snapshot1, -1);
658    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser2, snapshot1, 6);
659    assertTrue(hasUserNamespaceHdfsAcl(aclTable, grantUserName2, namespace));
660    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), grantUserName2, true, true);
661    assertFalse(hasUserTableHdfsAcl(aclTable, grantUserName1, table));
662    checkUserAclEntry(FS, helper.getPathHelper().getDataTableDir(table), grantUserName1, false,
663      false);
664    checkUserAclEntry(FS, helper.getPathHelper().getMobTableDir(table), grantUserName1, false,
665      false);
666    checkUserAclEntry(FS, helper.getPathHelper().getArchiveTableDir(table), grantUserName1, true,
667      false);
668
669    // check tmp table directory does not exist
670    Path tmpTableDir = helper.getPathHelper().getTmpTableDir(table);
671    assertFalse(FS.exists(tmpTableDir));
672    deleteTable(table);
673  }
674
675  @Test
676  public void testDeleteTable2(TestInfo testInfo) throws Exception {
677    String namespace1 = testInfo.getTestMethod().get().getName() + "1";
678    String namespace2 = testInfo.getTestMethod().get().getName() + "2";
679    String grantUser = testInfo.getTestMethod().get().getName();
680    TableName table = TableName.valueOf(namespace1, testInfo.getTestMethod().get().getName());
681
682    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
683    // grant user table permission
684    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUser, table, READ);
685    // grant user other namespace permission
686    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUser, namespace2, READ);
687    // delete table
688    admin.disableTable(table);
689    admin.deleteTable(table);
690    // grantUser should have namespace2's acl
691    assertFalse(hasUserTableHdfsAcl(aclTable, grantUser, table));
692    assertTrue(hasUserNamespaceHdfsAcl(aclTable, grantUser, namespace2));
693  }
694
695  @Test
696  public void testDeleteNamespace(TestInfo testInfo) throws Exception {
697    String grantUserName = testInfo.getTestMethod().get().getName();
698    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
699    String namespace = testInfo.getTestMethod().get().getName();
700    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
701    String snapshot = namespace + "t1";
702    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
703    // snapshot
704    snapshotAndWait(snapshot, table);
705    // grant namespace permission
706    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
707    // delete table
708    admin.disableTable(table);
709    admin.deleteTable(table);
710    // delete namespace
711    admin.deleteNamespace(namespace);
712    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
713    assertFalse(hasUserNamespaceHdfsAcl(aclTable, grantUserName, namespace));
714    checkUserAclEntry(FS, helper.getPathHelper().getArchiveNsDir(namespace), grantUserName, true,
715      false);
716
717    // check tmp namespace dir does not exist
718    assertFalse(FS.exists(helper.getPathHelper().getTmpNsDir(namespace)));
719    assertFalse(FS.exists(helper.getPathHelper().getDataNsDir(namespace)));
720    // assertFalse(fs.exists(FS, helper.getPathHelper().getMobDataNsDir(namespace)));
721    deleteTable(table);
722  }
723
724  @Test
725  public void testCleanArchiveTableDir(TestInfo testInfo) throws Exception {
726    final String grantUserName = testInfo.getTestMethod().get().getName();
727    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
728    String namespace = testInfo.getTestMethod().get().getName();
729    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
730    String snapshot = namespace + "t1";
731
732    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
733    snapshotAndWait(snapshot, table);
734    TestHDFSAclHelper.grantOnTable(TEST_UTIL, grantUserName, table, READ);
735    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
736
737    // HFileCleaner will not delete archive table directory even if it's a empty directory
738    HFileCleaner cleaner = TEST_UTIL.getHBaseCluster().getMaster().getHFileCleaner();
739    cleaner.choreForTesting();
740    Path archiveTableDir = HFileArchiveUtil.getTableArchivePath(rootDir, table);
741    assertTrue(FS.exists(archiveTableDir));
742    checkUserAclEntry(FS, helper.getTableRootPaths(table, false), grantUserName, true, true);
743
744    // Check SnapshotScannerHDFSAclCleaner method
745    assertTrue(SnapshotScannerHDFSAclCleaner.isArchiveTableDir(archiveTableDir));
746    assertTrue(SnapshotScannerHDFSAclCleaner.isArchiveNamespaceDir(archiveTableDir.getParent()));
747    assertTrue(
748      SnapshotScannerHDFSAclCleaner.isArchiveDataDir(archiveTableDir.getParent().getParent()));
749    assertFalse(SnapshotScannerHDFSAclCleaner
750      .isArchiveDataDir(archiveTableDir.getParent().getParent().getParent()));
751    deleteTable(table);
752  }
753
754  @Test
755  public void testModifyTable1(TestInfo testInfo) throws Exception {
756    String namespace = testInfo.getTestMethod().get().getName();
757    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName());
758    String snapshot = namespace + "t1";
759
760    String tableUserName = testInfo.getTestMethod().get().getName();
761    User tableUser = User.createUserForTesting(conf, tableUserName, new String[] {});
762    String tableUserName2 = tableUserName + "2";
763    User tableUser2 = User.createUserForTesting(conf, tableUserName2, new String[] {});
764    String tableUserName3 = tableUserName + "3";
765    User tableUser3 = User.createUserForTesting(conf, tableUserName3, new String[] {});
766    String nsUserName = tableUserName + "-ns";
767    User nsUser = User.createUserForTesting(conf, nsUserName, new String[] {});
768    String globalUserName = tableUserName + "-global";
769    User globalUser = User.createUserForTesting(conf, globalUserName, new String[] {});
770    String globalUserName2 = tableUserName + "-global-2";
771    User globalUser2 = User.createUserForTesting(conf, globalUserName2, new String[] {});
772
773    SecureTestUtil.grantGlobal(TEST_UTIL, globalUserName, READ);
774    TestHDFSAclHelper.createNamespace(TEST_UTIL, namespace);
775    SecureTestUtil.grantOnNamespace(TEST_UTIL, nsUserName, namespace, READ);
776    TableDescriptor td = TestHDFSAclHelper.createUserScanSnapshotDisabledTable(TEST_UTIL, table);
777    snapshotAndWait(snapshot, table);
778    SecureTestUtil.grantGlobal(TEST_UTIL, globalUserName2, READ);
779    TestHDFSAclHelper.grantOnTable(TEST_UTIL, tableUserName, table, READ);
780    SecureTestUtil.grantOnTable(TEST_UTIL, tableUserName2, table, TestHDFSAclHelper.COLUMN1, null,
781      READ);
782    TestHDFSAclHelper.grantOnTable(TEST_UTIL, tableUserName3, table, WRITE);
783
784    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser, snapshot, -1);
785    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser2, snapshot, -1);
786    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser3, snapshot, -1);
787    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, nsUser, snapshot, -1);
788    // Global permission is set before table is created, the acl is inherited
789    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, globalUser, snapshot, 6);
790    // Global permission is set after table is created, the table dir acl is skip
791    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, globalUser2, snapshot, -1);
792
793    // enable user scan snapshot
794    admin.modifyTable(TableDescriptorBuilder.newBuilder(td)
795      .setValue(SnapshotScannerHDFSAclHelper.ACL_SYNC_TO_HDFS_ENABLE, "true").build());
796    // check scan snapshot
797    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser, snapshot, 6);
798    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser2, snapshot, -1);
799    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser3, snapshot, -1);
800    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, nsUser, snapshot, 6);
801    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, globalUser, snapshot, 6);
802    // check acl table storage and ACLs in dirs
803    assertTrue(hasUserGlobalHdfsAcl(aclTable, globalUserName));
804    checkUserAclEntry(FS, helper.getGlobalRootPaths(), globalUserName, true, true);
805    assertTrue(hasUserNamespaceHdfsAcl(aclTable, nsUserName, namespace));
806    checkUserAclEntry(FS, helper.getNamespaceRootPaths(namespace), nsUserName, true, true);
807    assertTrue(hasUserTableHdfsAcl(aclTable, tableUserName, table));
808    checkUserAclEntry(FS, helper.getTableRootPaths(table, false), tableUserName, true, true);
809    for (String user : new String[] { tableUserName2, tableUserName3 }) {
810      assertFalse(hasUserTableHdfsAcl(aclTable, user, table));
811      checkUserAclEntry(FS, helper.getTableRootPaths(table, false), user, false, false);
812    }
813    deleteTable(table);
814  }
815
816  @Test
817  public void testModifyTable2(TestInfo testInfo) throws Exception {
818    String namespace = testInfo.getTestMethod().get().getName();
819    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".1");
820    String snapshot = namespace + "t1";
821    TableName table2 =
822      TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".2");
823
824    String tableUserName = testInfo.getTestMethod().get().getName();
825    User tableUser = User.createUserForTesting(conf, tableUserName, new String[] {});
826    String tableUserName2 = tableUserName + "2";
827    User tableUser2 = User.createUserForTesting(conf, tableUserName2, new String[] {});
828    String tableUserName3 = tableUserName + "3";
829    User tableUser3 = User.createUserForTesting(conf, tableUserName3, new String[] {});
830    String nsUserName = tableUserName + "-ns";
831    User nsUser = User.createUserForTesting(conf, nsUserName, new String[] {});
832    String globalUserName = tableUserName + "-global";
833    User globalUser = User.createUserForTesting(conf, globalUserName, new String[] {});
834    String globalUserName2 = tableUserName + "-global-2";
835    User globalUser2 = User.createUserForTesting(conf, globalUserName2, new String[] {});
836
837    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
838    SecureTestUtil.grantGlobal(TEST_UTIL, globalUserName, READ);
839    SecureTestUtil.grantGlobal(TEST_UTIL, globalUserName2, READ);
840    SecureTestUtil.grantOnNamespace(TEST_UTIL, nsUserName, namespace, READ);
841    TestHDFSAclHelper.grantOnTable(TEST_UTIL, tableUserName, table, READ);
842    SecureTestUtil.grantOnTable(TEST_UTIL, tableUserName2, table, TestHDFSAclHelper.COLUMN1, null,
843      READ);
844    TestHDFSAclHelper.grantOnTable(TEST_UTIL, tableUserName3, table, WRITE);
845
846    SecureTestUtil.grantOnNamespace(TEST_UTIL, tableUserName2, namespace, READ);
847    TestHDFSAclHelper.createTable(TEST_UTIL, table2);
848    TestHDFSAclHelper.grantOnTable(TEST_UTIL, tableUserName3, table2, READ);
849    // disable user scan snapshot
850    admin.modifyTable(TableDescriptorBuilder.newBuilder(admin.getDescriptor(table))
851      .setValue(SnapshotScannerHDFSAclHelper.ACL_SYNC_TO_HDFS_ENABLE, "false").build());
852    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser, snapshot, -1);
853    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser2, snapshot, -1);
854    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, tableUser3, snapshot, -1);
855    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, nsUser, snapshot, -1);
856    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, globalUser, snapshot, -1);
857    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, globalUser2, snapshot, -1);
858    // check access
859    String[] users = new String[] { globalUserName, globalUserName2, nsUserName, tableUserName,
860      tableUserName2, tableUserName3 };
861    for (Path path : helper.getTableRootPaths(table, false)) {
862      for (String user : users) {
863        checkUserAclEntry(FS, path, user, false, false);
864      }
865    }
866    String[] nsUsers = new String[] { globalUserName, globalUserName2, nsUserName };
867    for (Path path : helper.getNamespaceRootPaths(namespace)) {
868      checkUserAclEntry(FS, path, tableUserName, false, false);
869      checkUserAclEntry(FS, path, tableUserName2, true, true);
870      checkUserAclEntry(FS, path, tableUserName3, true, false);
871      for (String user : nsUsers) {
872        checkUserAclEntry(FS, path, user, true, true);
873      }
874    }
875    assertTrue(hasUserNamespaceHdfsAcl(aclTable, nsUserName, namespace));
876    assertTrue(hasUserNamespaceHdfsAcl(aclTable, tableUserName2, namespace));
877    assertFalse(hasUserTableHdfsAcl(aclTable, tableUserName, table));
878    deleteTable(table);
879    deleteTable(table2);
880  }
881
882  @Test
883  public void testRestartMaster(TestInfo testInfo) throws Exception {
884    final String grantUserName = testInfo.getTestMethod().get().getName();
885    User grantUser = User.createUserForTesting(conf, grantUserName, new String[] {});
886    String namespace = testInfo.getTestMethod().get().getName();
887    TableName table = TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".1");
888    TableName table2 =
889      TableName.valueOf(namespace, testInfo.getTestMethod().get().getName() + ".2");
890    String snapshot = namespace + "t1";
891    admin.createNamespace(NamespaceDescriptor.create(namespace).build());
892
893    // create table2
894    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table2);
895    // make some region files in tmp dir and check if master archive these region correctly
896    Path tmpTableDir = helper.getPathHelper().getTmpTableDir(table2);
897    // make a empty region dir, this is an error region
898    FS.mkdirs(new Path(tmpTableDir, "1"));
899    // copy regions from data dir, this is a valid region
900    for (Path regionDir : FSUtils.getRegionDirs(FS,
901      helper.getPathHelper().getDataTableDir(table2))) {
902      FSUtils.copyFilesParallel(FS, regionDir, FS,
903        new Path(tmpTableDir, regionDir.getName() + "abc"), conf, 1);
904    }
905    assertEquals(4, FS.listStatus(tmpTableDir).length);
906
907    // grant N(R)
908    SecureTestUtil.grantOnNamespace(TEST_UTIL, grantUserName, namespace, READ);
909    // restart cluster and tmp directory will not be deleted
910    TEST_UTIL.getMiniHBaseCluster().shutdown();
911    TEST_UTIL.restartHBaseCluster(1);
912    TEST_UTIL.waitUntilNoRegionsInTransition();
913
914    // reset the cached configs after restart
915    conf = TEST_UTIL.getConfiguration();
916    admin = TEST_UTIL.getAdmin();
917    helper = new SnapshotScannerHDFSAclHelper(conf, admin.getConnection());
918
919    Path tmpNsDir = helper.getPathHelper().getTmpNsDir(namespace);
920    assertTrue(FS.exists(tmpNsDir));
921    // check all regions in tmp table2 dir are archived
922    assertEquals(0, FS.listStatus(tmpTableDir).length);
923
924    // create table1 and snapshot
925    TestHDFSAclHelper.createTableAndPut(TEST_UTIL, table);
926    aclTable = TEST_UTIL.getConnection().getTable(PermissionStorage.ACL_TABLE_NAME);
927    snapshotAndWait(snapshot, table);
928    TestHDFSAclHelper.canUserScanSnapshot(TEST_UTIL, grantUser, snapshot, 6);
929    deleteTable(table);
930    deleteTable(table2);
931  }
932
933  static void checkUserAclEntry(FileSystem fs, List<Path> paths, String user,
934    boolean requireAccessAcl, boolean requireDefaultAcl) throws Exception {
935    for (Path path : paths) {
936      checkUserAclEntry(fs, path, user, requireAccessAcl, requireDefaultAcl);
937    }
938  }
939
940  static void checkUserAclEntry(FileSystem fs, Path path, String userName, boolean requireAccessAcl,
941    boolean requireDefaultAcl) throws IOException {
942    boolean accessAclEntry = false;
943    boolean defaultAclEntry = false;
944    if (fs.exists(path)) {
945      for (AclEntry aclEntry : fs.getAclStatus(path).getEntries()) {
946        String user = aclEntry.getName();
947        if (user != null && user.equals(userName)) {
948          if (aclEntry.getScope() == AclEntryScope.DEFAULT) {
949            defaultAclEntry = true;
950          } else if (aclEntry.getScope() == AclEntryScope.ACCESS) {
951            accessAclEntry = true;
952          }
953        }
954      }
955    }
956    String message = "require user: " + userName + ", path: " + path.toString() + " acl";
957    assertEquals(requireAccessAcl, accessAclEntry, message);
958    assertEquals(requireDefaultAcl, defaultAclEntry, message);
959  }
960
961  static void deleteTable(TableName tableName) {
962    try {
963      admin.disableTable(tableName);
964      admin.deleteTable(tableName);
965    } catch (IOException e) {
966      LOG.warn("Failed to delete table: {}", tableName);
967    }
968  }
969}