Skip to content

Commit 5160c62

Browse files
committed
HBASE-22580 Add a table attribute to make user scan snapshot feature configurable for table
1 parent 15ac781 commit 5160c62

File tree

4 files changed

+243
-90
lines changed

4 files changed

+243
-90
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/PermissionStorage.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@ public static ListMultimap<String, UserPermission> getNamespacePermissions(Confi
508508
false);
509509
}
510510

511+
public static ListMultimap<String, UserPermission> getGlobalPermissions(Configuration conf)
512+
throws IOException {
513+
return getPermissions(conf, null, null, null, null, null, false);
514+
}
515+
511516
/**
512517
* Reads user permission assignments stored in the <code>l:</code> column family of the first
513518
* table row in <code>_acl_</code>.

hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/SnapshotScannerHDFSAclController.java

Lines changed: 104 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.List;
2626
import java.util.Optional;
2727
import java.util.Set;
28+
import java.util.function.Supplier;
2829
import java.util.stream.Collectors;
2930

3031
import org.apache.hadoop.conf.Configuration;
@@ -58,20 +59,22 @@
5859
import org.apache.hadoop.hbase.master.MasterServices;
5960
import org.apache.hadoop.hbase.security.User;
6061
import org.apache.hadoop.hbase.security.UserProvider;
61-
import org.apache.hadoop.hbase.security.access.Permission.Action;
6262
import org.apache.hadoop.hbase.security.access.SnapshotScannerHDFSAclHelper.PathHelper;
6363
import org.apache.hadoop.hbase.util.Bytes;
6464
import org.apache.hadoop.hbase.util.Pair;
6565
import org.apache.yetus.audience.InterfaceAudience;
6666
import org.slf4j.Logger;
6767
import 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

Comments
 (0)