2525import java .util .List ;
2626import java .util .Optional ;
2727import java .util .Set ;
28+ import java .util .function .Supplier ;
2829import java .util .stream .Collectors ;
2930
3031import org .apache .hadoop .conf .Configuration ;
5859import org .apache .hadoop .hbase .master .MasterServices ;
5960import org .apache .hadoop .hbase .security .User ;
6061import org .apache .hadoop .hbase .security .UserProvider ;
61- import org .apache .hadoop .hbase .security .access .Permission .Action ;
6262import org .apache .hadoop .hbase .security .access .SnapshotScannerHDFSAclHelper .PathHelper ;
6363import org .apache .hadoop .hbase .util .Bytes ;
6464import org .apache .hadoop .hbase .util .Pair ;
6565import org .apache .yetus .audience .InterfaceAudience ;
6666import org .slf4j .Logger ;
6767import org .slf4j .LoggerFactory ;
6868
69+ import org .apache .hbase .thirdparty .com .google .common .annotations .VisibleForTesting ;
70+ import org .apache .hbase .thirdparty .com .google .common .collect .Sets ;
71+
6972/**
7073 * Set HDFS ACLs to hFiles to make HBase granted users have permission to scan snapshot
7174 * <p>
7275 * To use this feature, please mask sure HDFS config:
7376 * <ul>
74- * <li>dfs.permissions .enabled = true</li>
77+ * <li>dfs.namenode.acls .enabled = true</li>
7578 * <li>fs.permissions.umask-mode = 027 (or smaller umask than 027)</li>
7679 * </ul>
7780 * </p>
@@ -103,7 +106,9 @@ public class SnapshotScannerHDFSAclController implements MasterCoprocessor, Mast
103106 private SnapshotScannerHDFSAclHelper hdfsAclHelper = null ;
104107 private PathHelper pathHelper = null ;
105108 private FileSystem fs = null ;
109+ private MasterServices masterServices = null ;
106110 private volatile boolean initialized = false ;
111+ private volatile boolean aclTableInitialized = false ;
107112 /** Provider for mapping principal names to Users */
108113 private UserProvider userProvider ;
109114
@@ -121,7 +126,7 @@ public void preMasterInitialization(ObserverContext<MasterCoprocessorEnvironment
121126 if (!(mEnv instanceof HasMasterServices )) {
122127 throw new IOException ("Does not implement HMasterServices" );
123128 }
124- MasterServices masterServices = ((HasMasterServices ) mEnv ).getMasterServices ();
129+ masterServices = ((HasMasterServices ) mEnv ).getMasterServices ();
125130 hdfsAclHelper = new SnapshotScannerHDFSAclHelper (masterServices .getConfiguration (),
126131 masterServices .getConnection ());
127132 pathHelper = hdfsAclHelper .getPathHelper ();
@@ -138,7 +143,7 @@ public void preMasterInitialization(ObserverContext<MasterCoprocessorEnvironment
138143
139144 @ Override
140145 public void postStartMaster (ObserverContext <MasterCoprocessorEnvironment > c ) throws IOException {
141- if (checkInitialized () ) {
146+ if (initialized ) {
142147 try (Admin admin = c .getEnvironment ().getConnection ().getAdmin ()) {
143148 if (admin .tableExists (PermissionStorage .ACL_TABLE_NAME )) {
144149 // Check if hbase acl table has 'm' CF, if not, add 'm' CF
@@ -151,6 +156,8 @@ public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> c) thr
151156 .setColumnFamily (ColumnFamilyDescriptorBuilder
152157 .newBuilder (SnapshotScannerHDFSAclStorage .HDFS_ACL_FAMILY ).build ());
153158 admin .modifyTable (builder .build ());
159+ } else {
160+ aclTableInitialized = true ;
154161 }
155162 } else {
156163 throw new TableNotFoundException ("Table " + PermissionStorage .ACL_TABLE_NAME
@@ -163,26 +170,21 @@ public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> c) thr
163170
164171 @ Override
165172 public void preStopMaster (ObserverContext <MasterCoprocessorEnvironment > c ) {
166- if (checkInitialized () ) {
173+ if (initialized ) {
167174 hdfsAclHelper .close ();
168175 }
169176 }
170177
171178 @ Override
172179 public void postCompletedCreateTableAction (ObserverContext <MasterCoprocessorEnvironment > c ,
173180 TableDescriptor desc , RegionInfo [] regions ) throws IOException {
174- if (! desc . getTableName (). isSystemTable () && checkInitialized ( )) {
181+ if (checkTable ( desc , () -> "createTable " + desc . getTableName () )) {
175182 TableName tableName = desc .getTableName ();
176- List <Path > paths = hdfsAclHelper .getTableRootPaths (tableName , false );
177- for (Path path : paths ) {
178- if (!fs .exists (path )) {
179- fs .mkdirs (path );
180- }
181- }
183+ hdfsAclHelper .createTableDirectories (tableName );
182184 // Add table owner HDFS acls
183185 String owner =
184186 desc .getOwnerString () == null ? getActiveUser (c ).getShortName () : desc .getOwnerString ();
185- hdfsAclHelper .addTableAcl (tableName , owner );
187+ hdfsAclHelper .addTableAcl (tableName , Sets . newHashSet ( owner ) );
186188 try (Table aclTable =
187189 c .getEnvironment ().getConnection ().getTable (PermissionStorage .ACL_TABLE_NAME )) {
188190 SnapshotScannerHDFSAclStorage .addUserTableHdfsAcl (aclTable , owner , tableName );
@@ -193,7 +195,7 @@ public void postCompletedCreateTableAction(ObserverContext<MasterCoprocessorEnvi
193195 @ Override
194196 public void postCreateNamespace (ObserverContext <MasterCoprocessorEnvironment > c ,
195197 NamespaceDescriptor ns ) throws IOException {
196- if (checkInitialized ()) {
198+ if (checkInitialized (() -> "createNamespace " + ns . getName () )) {
197199 List <Path > paths = hdfsAclHelper .getNamespaceRootPaths (ns .getName ());
198200 for (Path path : paths ) {
199201 if (!fs .exists (path )) {
@@ -206,23 +208,23 @@ public void postCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> c,
206208 @ Override
207209 public void postCompletedSnapshotAction (ObserverContext <MasterCoprocessorEnvironment > c ,
208210 SnapshotDescription snapshot , TableDescriptor tableDescriptor ) throws IOException {
209- if (! tableDescriptor . getTableName (). isSystemTable () && checkInitialized ( )) {
211+ if (checkTable ( tableDescriptor , () -> "snapshot " + snapshot . getName () )) {
210212 hdfsAclHelper .snapshotAcl (snapshot );
211213 }
212214 }
213215
214216 @ Override
215217 public void postCompletedTruncateTableAction (ObserverContext <MasterCoprocessorEnvironment > c ,
216218 TableName tableName ) throws IOException {
217- if (! tableName . isSystemTable () && checkInitialized ( )) {
219+ if (checkTable ( tableName , () -> "truncateTable " + tableName )) {
218220 hdfsAclHelper .resetTableAcl (tableName );
219221 }
220222 }
221223
222224 @ Override
223225 public void postDeleteTable (ObserverContext <MasterCoprocessorEnvironment > ctx ,
224226 TableName tableName ) throws IOException {
225- if (! tableName . isSystemTable () && checkInitialized ( )) {
227+ if (checkTable ( tableName , () -> "deleteTable " + tableName )) {
226228 /*
227229 * remove table user access HDFS acl from namespace directory if the user has no permissions
228230 * of global, ns of the table or other tables of the ns, eg: Bob has 'ns1:t1' read permission,
@@ -263,10 +265,36 @@ public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
263265 }
264266 }
265267
268+ @ Override
269+ public void postModifyTable (ObserverContext <MasterCoprocessorEnvironment > ctx ,
270+ TableName tableName , TableDescriptor oldDescriptor , TableDescriptor currentDescriptor )
271+ throws IOException {
272+ if (checkTable (currentDescriptor , () -> "modifyTable " + tableName )
273+ && !hdfsAclHelper .isTableUserScanSnapshotEnabled (oldDescriptor )) {
274+ hdfsAclHelper .createTableDirectories (tableName );
275+ Set <String > users = new HashSet <>();
276+ users .addAll (hdfsAclHelper .getUsersWithGlobalReadAction ());
277+ users .addAll (hdfsAclHelper .getUsersWithNamespaceReadAction (tableName .getNamespaceAsString ()));
278+ Set <String > userWithTableReadPermission =
279+ hdfsAclHelper .getUsersWithTableReadAction (tableName );
280+ users .addAll (userWithTableReadPermission );
281+ hdfsAclHelper .addTableAcl (tableName , users );
282+ try (Table aclTable =
283+ ctx .getEnvironment ().getConnection ().getTable (PermissionStorage .ACL_TABLE_NAME )) {
284+ for (String user : userWithTableReadPermission ) {
285+ SnapshotScannerHDFSAclStorage .addUserTableHdfsAcl (aclTable , user , tableName );
286+ }
287+ }
288+ } else if (!aclTableInitialized
289+ && Bytes .equals (PermissionStorage .ACL_GLOBAL_NAME , tableName .getName ())) {
290+ aclTableInitialized = true ;
291+ }
292+ }
293+
266294 @ Override
267295 public void postDeleteNamespace (ObserverContext <MasterCoprocessorEnvironment > ctx ,
268296 String namespace ) throws IOException {
269- if (checkInitialized ()) {
297+ if (checkInitialized (() -> "deleteNamespace " + namespace )) {
270298 try (Table aclTable =
271299 ctx .getEnvironment ().getConnection ().getTable (PermissionStorage .ACL_TABLE_NAME )) {
272300 SnapshotScannerHDFSAclStorage .deleteNamespaceHdfsAcl (aclTable , namespace );
@@ -292,7 +320,8 @@ public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ct
292320 @ Override
293321 public void postGrant (ObserverContext <MasterCoprocessorEnvironment > c ,
294322 UserPermission userPermission , boolean mergeExistingPermissions ) throws IOException {
295- if (!checkInitialized ()) {
323+ if (!checkInitialized (() -> "grant " + userPermission + ", merge existing permissions "
324+ + mergeExistingPermissions )) {
296325 return ;
297326 }
298327 try (Table aclTable =
@@ -302,7 +331,7 @@ public void postGrant(ObserverContext<MasterCoprocessorEnvironment> c,
302331 switch (userPermission .getAccessScope ()) {
303332 case GLOBAL :
304333 UserPermission perm = getUserGlobalPermission (conf , userName );
305- if (perm != null && containReadPermission (perm )) {
334+ if (perm != null && hdfsAclHelper . containReadAction (perm )) {
306335 if (!isHdfsAclSet (aclTable , userName )) {
307336 Pair <Set <String >, Set <TableName >> namespaceAndTable =
308337 SnapshotScannerHDFSAclStorage .getUserNamespaceAndTable (aclTable , userName );
@@ -322,7 +351,7 @@ public void postGrant(ObserverContext<MasterCoprocessorEnvironment> c,
322351 case NAMESPACE :
323352 String namespace = ((NamespacePermission ) userPermission .getPermission ()).getNamespace ();
324353 UserPermission nsPerm = getUserNamespacePermission (conf , userName , namespace );
325- if (nsPerm != null && containReadPermission (nsPerm )) {
354+ if (nsPerm != null && hdfsAclHelper . containReadAction (nsPerm )) {
326355 if (!isHdfsAclSet (aclTable , userName , namespace )) {
327356 Set <TableName > skipTables = SnapshotScannerHDFSAclStorage
328357 .getUserNamespaceAndTable (aclTable , userName ).getSecond ();
@@ -336,23 +365,20 @@ public void postGrant(ObserverContext<MasterCoprocessorEnvironment> c,
336365 }
337366 break ;
338367 case TABLE :
339- TableName tableName = ((TablePermission ) userPermission .getPermission ()).getTableName ();
340- UserPermission tPerm = getUserTablePermission (conf , userName , tableName );
341- if (tPerm != null ) {
342- TablePermission tablePermission = (TablePermission ) tPerm .getPermission ();
343- if (tablePermission .hasFamily () || tablePermission .hasQualifier ()) {
344- break ;
345- }
346- }
347- if (tPerm != null && containReadPermission (tPerm )) {
348- if (!isHdfsAclSet (aclTable , userName , tableName )) {
349- hdfsAclHelper .grantAcl (userPermission , new HashSet <>(0 ), new HashSet <>(0 ));
368+ TablePermission tablePerm = (TablePermission ) userPermission .getPermission ();
369+ if (checkTableAndPermission (tablePerm )) {
370+ TableName tableName = tablePerm .getTableName ();
371+ UserPermission tPerm = getUserTablePermission (conf , userName , tableName );
372+ if (tPerm != null && hdfsAclHelper .containReadAction (tPerm )) {
373+ if (!isHdfsAclSet (aclTable , userName , tableName )) {
374+ hdfsAclHelper .grantAcl (userPermission , new HashSet <>(0 ), new HashSet <>(0 ));
375+ }
376+ SnapshotScannerHDFSAclStorage .addUserTableHdfsAcl (aclTable , userName , tableName );
377+ } else {
378+ // The merged user permission doesn't contain READ, so remove user table HDFS acls if
379+ // it's set
380+ removeUserTableHdfsAcl (aclTable , userName , tableName , userPermission );
350381 }
351- SnapshotScannerHDFSAclStorage .addUserTableHdfsAcl (aclTable , userName , tableName );
352- } else {
353- // The merged user permission doesn't contain READ, so remove user table HDFS acls if
354- // it's set
355- removeUserTableHdfsAcl (aclTable , userName , tableName , userPermission );
356382 }
357383 break ;
358384 default :
@@ -365,32 +391,34 @@ public void postGrant(ObserverContext<MasterCoprocessorEnvironment> c,
365391 @ Override
366392 public void postRevoke (ObserverContext <MasterCoprocessorEnvironment > c ,
367393 UserPermission userPermission ) throws IOException {
368- if (checkInitialized ()) {
394+ if (checkInitialized (() -> "revoke " + userPermission )) {
369395 try (Table aclTable =
370396 c .getEnvironment ().getConnection ().getTable (PermissionStorage .ACL_TABLE_NAME )) {
371397 String userName = userPermission .getUser ();
372398 Configuration conf = c .getEnvironment ().getConfiguration ();
373399 switch (userPermission .getAccessScope ()) {
374400 case GLOBAL :
375401 UserPermission userGlobalPerm = getUserGlobalPermission (conf , userName );
376- if (userGlobalPerm == null || !containReadPermission (userGlobalPerm )) {
402+ if (userGlobalPerm == null || !hdfsAclHelper . containReadAction (userGlobalPerm )) {
377403 removeUserGlobalHdfsAcl (aclTable , userName , userPermission );
378404 }
379405 break ;
380406 case NAMESPACE :
381407 NamespacePermission nsPerm = (NamespacePermission ) userPermission .getPermission ();
382408 UserPermission userNsPerm =
383409 getUserNamespacePermission (conf , userName , nsPerm .getNamespace ());
384- if (userNsPerm == null || !containReadPermission (userNsPerm )) {
410+ if (userNsPerm == null || !hdfsAclHelper . containReadAction (userNsPerm )) {
385411 removeUserNamespaceHdfsAcl (aclTable , userName , nsPerm .getNamespace (), userPermission );
386412 }
387413 break ;
388414 case TABLE :
389415 TablePermission tPerm = (TablePermission ) userPermission .getPermission ();
390- UserPermission userTablePerm =
391- getUserTablePermission (conf , userName , tPerm .getTableName ());
392- if (userTablePerm == null || !containReadPermission (userTablePerm )) {
393- removeUserTableHdfsAcl (aclTable , userName , tPerm .getTableName (), userPermission );
416+ if (checkTableAndPermission (tPerm )) {
417+ TableName tableName = tPerm .getTableName ();
418+ UserPermission userTablePerm = getUserTablePermission (conf , userName , tableName );
419+ if (userTablePerm == null || !hdfsAclHelper .containReadAction (userTablePerm )) {
420+ removeUserTableHdfsAcl (aclTable , userName , tableName , userPermission );
421+ }
394422 }
395423 break ;
396424 default :
@@ -442,42 +470,28 @@ private void removeUserTableHdfsAcl(Table aclTable, String userName, TableName t
442470 }
443471 }
444472
445- private boolean containReadPermission (UserPermission userPermission ) {
446- if (userPermission != null ) {
447- return Arrays .stream (userPermission .getPermission ().getActions ())
448- .anyMatch (action -> action == Action .READ );
449- }
450- return false ;
451- }
452-
453473 private UserPermission getUserGlobalPermission (Configuration conf , String userName )
454474 throws IOException {
455475 List <UserPermission > permissions = PermissionStorage .getUserPermissions (conf ,
456476 PermissionStorage .ACL_GLOBAL_NAME , null , null , userName , true );
457- if (permissions != null && permissions .size () > 0 ) {
458- return permissions .get (0 );
459- }
460- return null ;
477+ return permissions .size () > 0 ? permissions .get (0 ) : null ;
461478 }
462479
463480 private UserPermission getUserNamespacePermission (Configuration conf , String userName ,
464481 String namespace ) throws IOException {
465482 List <UserPermission > permissions =
466483 PermissionStorage .getUserNamespacePermissions (conf , namespace , userName , true );
467- if (permissions != null && permissions .size () > 0 ) {
468- return permissions .get (0 );
469- }
470- return null ;
484+ return permissions .size () > 0 ? permissions .get (0 ) : null ;
471485 }
472486
473487 private UserPermission getUserTablePermission (Configuration conf , String userName ,
474488 TableName tableName ) throws IOException {
475- List <UserPermission > permissions =
476- PermissionStorage .getUserTablePermissions (conf , tableName , null , null , userName , true );
477- if ( permissions != null && permissions . size () > 0 ) {
478- return permissions . get ( 0 );
479- }
480- return null ;
489+ List <UserPermission > permissions = PermissionStorage
490+ .getUserTablePermissions (conf , tableName , null , null , userName , true ). stream ()
491+ . filter ( userPermission -> hdfsAclHelper
492+ . checkTablePermission (( TablePermission ) userPermission . getPermission ()))
493+ . collect ( Collectors . toList ());
494+ return permissions . size () > 0 ? permissions . get ( 0 ) : null ;
481495 }
482496
483497 private boolean isHdfsAclSet (Table aclTable , String userName ) throws IOException {
@@ -513,12 +527,32 @@ private boolean isHdfsAclSet(Table aclTable, String userName, String namespace,
513527 return isSet ;
514528 }
515529
516- private boolean checkInitialized () {
530+ @ VisibleForTesting
531+ boolean checkInitialized (Supplier <String > operation ) {
517532 if (initialized ) {
518- return true ;
519- } else {
520- return false ;
533+ if (aclTableInitialized ) {
534+ return true ;
535+ } else {
536+ LOG .warn ("Skip set HDFS acls because acl table is not initialized when " + operation .get ());
537+ }
521538 }
539+ return false ;
540+ }
541+
542+ private boolean checkTableAndPermission (TablePermission tablePermission ) throws IOException {
543+ return checkTable (tablePermission .getTableName (), () -> "" )
544+ && hdfsAclHelper .checkTablePermission (tablePermission );
545+ }
546+
547+ private boolean checkTable (TableName tableName , Supplier <String > operation ) throws IOException {
548+ return !tableName .isSystemTable () && checkInitialized (operation ) && hdfsAclHelper
549+ .isTableUserScanSnapshotEnabled (masterServices .getTableDescriptors ().get (tableName ));
550+ }
551+
552+ private boolean checkTable (TableDescriptor tableDescriptor , Supplier <String > operation ) {
553+ TableName tableName = tableDescriptor .getTableName ();
554+ return !tableName .isSystemTable () && checkInitialized (operation )
555+ && hdfsAclHelper .isTableUserScanSnapshotEnabled (tableDescriptor );
522556 }
523557
524558 private User getActiveUser (ObserverContext <?> ctx ) throws IOException {
0 commit comments