diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java
index 456f44bac113..654818a1aa5a 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java
@@ -218,6 +218,16 @@ public final class OzoneConfigKeys {
public static final String OZONE_READONLY_ADMINISTRATORS_GROUPS =
"ozone.readonly.administrators.groups";
+ public static final String OZONE_BLACKLIST_USERS =
+ "ozone.blacklist.users";
+ public static final String OZONE_BLACKLIST_GROUPS =
+ "ozone.blacklist.groups";
+
+ public static final String OZONE_READ_BLACKLIST_USERS =
+ "ozone.read.blacklist.users";
+ public static final String OZONE_READ_BLACKLIST_GROUPS =
+ "ozone.read.blacklist.groups";
+
/**
* Used only for testing purpose. Results in making every user an admin.
* */
diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 6c4bd26ebe88..a510a84424d1 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -1903,6 +1903,46 @@
+
+ ozone.blacklist.users
+
+
+ Ozone blacklisted users delimited by the comma.
+ If set, This is the list of users that are not allowed to do any operations even
+ if the blacklisted user is also under (readonly) admin / admin group,
+
+
+
+
+ ozone.blacklist.groups
+
+
+ Ozone blacklisted groups delimited by the comma.
+ If set, This is the list of groups that are not allowed to do any operations even
+ if the blacklisted user is also under (readonly) admin / admin group,
+
+
+
+
+ ozone.read.blacklist.users
+
+
+ Ozone read blacklist users delimited by the comma.
+ If set, This is the list of users are not allowed to do any read operations even
+ if the blacklisted user is also under (readonly) admin / admin group.
+
+
+
+
+ ozone.read.blacklist.groups
+
+
+ Ozone read blacklist groups delimited by the comma.
+ If set, This is the list of groups are not allowed to do any read operations even
+ if the blacklisted user is also under (readonly) admin / admin group.
+
+
+
ozone.s3g.volume.name
s3v
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneBlacklist.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneBlacklist.java
new file mode 100644
index 000000000000..8f48030d9ebb
--- /dev/null
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/server/OzoneBlacklist.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.server;
+
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLACKLIST_GROUPS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLACKLIST_USERS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READ_BLACKLIST_GROUPS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READ_BLACKLIST_USERS;
+
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.StringUtils;
+
+/**
+ * This class contains the blacklisted user information, username and group
+ * and is able to check whether the provided {@link UserGroupInformation}
+ * is blacklisted.
+ *
+ * The implementation is similar to {@link OzoneAdmins}.
+ */
+public class OzoneBlacklist {
+
+ private volatile Set blacklistUsernames;
+
+ private volatile Set blacklistGroups;
+
+ public OzoneBlacklist(Collection blacklistUsernames) {
+ this(blacklistUsernames, null);
+ }
+
+ public OzoneBlacklist(Collection blacklistUsernames,
+ Collection blacklistGroups) {
+ setBlacklistUsernames(blacklistUsernames);
+ this.blacklistGroups = blacklistGroups != null
+ ? Collections.unmodifiableSet(new LinkedHashSet<>(blacklistGroups))
+ : Collections.emptySet();
+ }
+
+ /**
+ * Returns an OzoneBlacklist instance configured with blacklisted users
+ * and groups from the provided configuration.
+ *
+ * @param configuration the configuration settings to apply.
+ * @return a configured OzoneBlacklist instance.
+ */
+ public static OzoneBlacklist getOzoneBlacklist(
+ OzoneConfiguration configuration) {
+ Collection blacklistUsernames =
+ getOzoneBlacklistUsersFromConfig(configuration);
+ Collection blacklistGroupNames =
+ getOzoneBlacklistGroupsFromConfig(configuration);
+ return new OzoneBlacklist(blacklistUsernames, blacklistGroupNames);
+ }
+
+ /**
+ * Creates and returns a read-only blacklist object. This object includes the
+ * read blacklisted users and user groups obtained from the Ozone
+ * configuration.
+ *
+ * @param configuration the configuration settings to apply.
+ * @return a configured OzoneBlacklist instance.
+ */
+ public static OzoneBlacklist getReadonlyBlacklist(
+ OzoneConfiguration configuration) {
+ Collection omReadBlacklistUser =
+ getOzoneReadBlacklistUsersFromConfig(configuration);
+ Collection omReadBlacklistGroups =
+ getOzoneReadBlacklistGroupsFromConfig(configuration);
+ return new OzoneBlacklist(omReadBlacklistUser, omReadBlacklistGroups);
+ }
+
+ /**
+ * Check ozone blacklist, throws exception if user is blacklisted.
+ */
+ public void checkBlacklist(UserGroupInformation ugi)
+ throws AccessControlException {
+ if (ugi != null && isBlacklisted(ugi)) {
+ throw new AccessControlException("Access denied for user "
+ + ugi.getUserName() + ". User is blacklisted.");
+ }
+ }
+
+ private boolean hasBlacklistGroup(Collection userGroups) {
+ return !Sets.intersection(blacklistGroups,
+ new LinkedHashSet<>(userGroups)).isEmpty();
+ }
+
+ /**
+ * Check whether the provided {@link UserGroupInformation user}
+ * is blacklisted.
+ *
+ * @param user the {@link UserGroupInformation}.
+ * @return true if the user is blacklisted, otherwise false.
+ */
+ public boolean isBlacklisted(UserGroupInformation user) {
+ return user != null
+ && (blacklistUsernames.contains(user.getShortUserName())
+ || hasBlacklistGroup(user.getGroups()));
+ }
+
+ public Collection getBlacklistGroups() {
+ return blacklistGroups;
+ }
+
+ public Set getBlacklistUsernames() {
+ return blacklistUsernames;
+ }
+
+ public void setBlacklistUsernames(
+ Collection blacklistUsernames) {
+ this.blacklistUsernames = blacklistUsernames != null
+ ? Collections.unmodifiableSet(
+ new LinkedHashSet<>(blacklistUsernames))
+ : Collections.emptySet();
+ }
+
+ /**
+ * Return list of blacklisted users from config.
+ *
+ * @param conf the configuration settings to apply.
+ */
+ public static Collection getOzoneBlacklistUsersFromConfig(
+ OzoneConfiguration conf) {
+ return conf.getTrimmedStringCollection(OZONE_BLACKLIST_USERS);
+ }
+
+ /**
+ * Return list of blacklisted users from config value.
+ * @param valueString the configuration value.
+ */
+ public static Collection getOzoneBlacklistUsersFromConfigValue(
+ String valueString) {
+ return StringUtils.getTrimmedStringCollection(valueString);
+ }
+
+ /**
+ * Return list of blacklist Groups from config.
+ *
+ * @param configuration the configuration settings to apply.
+ */
+ public static Collection getOzoneBlacklistGroupsFromConfig(
+ OzoneConfiguration configuration) {
+ return configuration.getTrimmedStringCollection(OZONE_BLACKLIST_GROUPS);
+ }
+
+ /**
+ * Return list of blacklisted groups from config value.
+ * @param valueString the configuration value.
+ */
+ public static Collection getOzoneBlacklistGroupsFromConfigValue(
+ String valueString) {
+ return StringUtils.getTrimmedStringCollection(valueString);
+ }
+
+ /**
+ * Return list of Ozone Read only blacklisted users from config.
+ *
+ * @param conf the configuration settings to apply.
+ */
+ public static Collection getOzoneReadBlacklistUsersFromConfig(
+ OzoneConfiguration conf) {
+ return conf.getTrimmedStringCollection(OZONE_READ_BLACKLIST_USERS);
+ }
+
+ /**
+ * Return list of Ozone Read only blacklisted users from config value.
+ * @param valueString the configuration value.
+ */
+ public static Collection getOzoneReadBlacklistUsersFromConfigValue(
+ String valueString) {
+ return StringUtils.getTrimmedStringCollection(valueString);
+ }
+
+ /**
+ * Return list of Ozone Read only blacklisted groups from config.
+ *
+ * @param conf the configuration settings to apply.
+ */
+ public static Collection getOzoneReadBlacklistGroupsFromConfig(
+ OzoneConfiguration conf) {
+ return conf.getTrimmedStringCollection(OZONE_READ_BLACKLIST_GROUPS);
+ }
+
+ /**
+ * Return list of Ozone Read only blacklisted groups from config value.
+ * @param valueString the configuration value.
+ */
+ public static Collection getOzoneReadBlacklistGroupsFromConfigValue(
+ String valueString) {
+ return StringUtils.getTrimmedStringCollection(valueString);
+ }
+
+ public void setBlacklistGroups(Collection blacklistGroups) {
+ this.blacklistGroups = blacklistGroups != null
+ ? Collections.unmodifiableSet(new LinkedHashSet<>(blacklistGroups))
+ : Collections.emptySet();
+ }
+}
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/reconfig/TestOmReconfiguration.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/reconfig/TestOmReconfiguration.java
index fb46edf0bdf0..d37cbd2619c3 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/reconfig/TestOmReconfiguration.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/reconfig/TestOmReconfiguration.java
@@ -19,7 +19,11 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLACKLIST_GROUPS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLACKLIST_USERS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READONLY_ADMINISTRATORS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READ_BLACKLIST_GROUPS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READ_BLACKLIST_USERS;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DIR_DELETING_SERVICE_INTERVAL;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_KEY_DELETING_LIMIT_PER_TASK;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_SNAPSHOT_SST_FILTERING_SERVICE_INTERVAL;
@@ -64,6 +68,10 @@ void reconfigurableProperties() {
.add(OZONE_THREAD_NUMBER_DIR_DELETION)
.add(OZONE_SNAPSHOT_SST_FILTERING_SERVICE_INTERVAL)
.addAll(new OmConfig().reconfigurableProperties())
+ .add(OZONE_BLACKLIST_USERS)
+ .add(OZONE_BLACKLIST_GROUPS)
+ .add(OZONE_READ_BLACKLIST_USERS)
+ .add(OZONE_READ_BLACKLIST_GROUPS)
.build();
assertProperties(getSubject(), expected);
@@ -92,6 +100,70 @@ void readOnlyAdmins() throws ReconfigurationException {
cluster().getOzoneManager().getOmReadOnlyAdminUsernames());
}
+ @Test
+ void blacklistUsers() throws ReconfigurationException {
+ final String newValue = RandomStringUtils.secure().nextAlphabetic(10);
+
+ getSubject().reconfigureProperty(OZONE_BLACKLIST_USERS, newValue);
+
+ assertEquals(
+ ImmutableSet.of(newValue),
+ cluster().getOzoneManager().getOmBlacklistUsernames());
+ }
+
+ @Test
+ void blacklistGroups() throws ReconfigurationException {
+ String groupA = "groupA";
+ String groupB = "groupB";
+ getSubject().reconfigureProperty(OZONE_BLACKLIST_GROUPS, groupA);
+ assertTrue(
+ cluster().getOzoneManager().getOmBlacklistGroups().contains(groupA),
+ groupA + " should be a blacklist group");
+
+ getSubject().reconfigureProperty(OZONE_BLACKLIST_GROUPS, groupB);
+ assertFalse(
+ cluster().getOzoneManager().getOmBlacklistGroups().contains(groupA),
+ groupA + " should NOT be a blacklist group");
+ assertTrue(
+ cluster().getOzoneManager().getOmBlacklistGroups().contains(groupB),
+ groupB + " should be a blacklist group");
+ }
+
+ @Test
+ void readBlacklistUsers() throws ReconfigurationException {
+ final String newValue = RandomStringUtils.secure().nextAlphabetic(10);
+
+ getSubject().reconfigureProperty(OZONE_READ_BLACKLIST_USERS,
+ newValue);
+
+ assertEquals(
+ ImmutableSet.of(newValue),
+ cluster().getOzoneManager().getOmReadOnlyBlacklistUsernames());
+ }
+
+ @Test
+ void readBlacklistGroups() throws ReconfigurationException {
+ String groupA = "readonlyBlacklistGroupA";
+ String groupB = "readonlyBlacklistGroupB";
+ getSubject().reconfigureProperty(OZONE_READ_BLACKLIST_GROUPS,
+ groupA);
+ assertTrue(
+ cluster().getOzoneManager().getOmReadonlyBlacklistGroups()
+ .contains(groupA),
+ groupA + " should be a readOnly blacklist group");
+
+ getSubject().reconfigureProperty(OZONE_READ_BLACKLIST_GROUPS,
+ groupB);
+ assertFalse(
+ cluster().getOzoneManager().getOmReadonlyBlacklistGroups()
+ .contains(groupA),
+ groupA + " should NOT be a readOnly blacklist group");
+ assertTrue(
+ cluster().getOzoneManager().getOmReadonlyBlacklistGroups()
+ .contains(groupB),
+ groupB + " should be a readOnly blacklist group");
+ }
+
@Test
public void maxListSize() throws ReconfigurationException {
final long initialValue = cluster().getOzoneManager().getConfig().getMaxListSize();
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 0587b71f239c..e42c1d76761c 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -32,11 +32,15 @@
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ACL_ENABLED_DEFAULT;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLACKLIST_GROUPS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLACKLIST_USERS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_FLEXIBLE_FQDN_RESOLUTION_ENABLED;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_FLEXIBLE_FQDN_RESOLUTION_ENABLED_DEFAULT;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_KEY_PREALLOCATION_BLOCKS_MAX;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_KEY_PREALLOCATION_BLOCKS_MAX_DEFAULT;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READONLY_ADMINISTRATORS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READ_BLACKLIST_GROUPS;
+import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_READ_BLACKLIST_USERS;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE_DEFAULT;
import static org.apache.hadoop.ozone.OzoneConsts.DB_TRANSIENT_MARKER;
@@ -197,6 +201,7 @@
import org.apache.hadoop.hdds.security.token.OzoneBlockTokenSecretManager;
import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.hdds.server.OzoneAdmins;
+import org.apache.hadoop.hdds.server.OzoneBlacklist;
import org.apache.hadoop.hdds.server.ServiceRuntimeInfoImpl;
import org.apache.hadoop.hdds.server.http.RatisDropwizardExports;
import org.apache.hadoop.hdds.utils.HAUtils;
@@ -412,6 +417,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
private final OzoneAdmins omAdmins;
private final OzoneAdmins readOnlyAdmins;
private final OzoneAdmins s3OzoneAdmins;
+ private final OzoneBlacklist omBlacklist;
+ private final OzoneBlacklist readBlacklist;
private final OMMetrics metrics;
private final OmSnapshotInternalMetrics omSnapshotIntMetrics;
@@ -531,7 +538,11 @@ private OzoneManager(OzoneConfiguration conf, StartupOption startupOption)
.register(OZONE_KEY_DELETING_LIMIT_PER_TASK,
this::reconfOzoneKeyDeletingLimitPerTask)
.register(OZONE_DIR_DELETING_SERVICE_INTERVAL, this::reconfOzoneDirDeletingServiceInterval)
- .register(OZONE_THREAD_NUMBER_DIR_DELETION, this::reconfOzoneThreadNumberDirDeletion);
+ .register(OZONE_THREAD_NUMBER_DIR_DELETION, this::reconfOzoneThreadNumberDirDeletion)
+ .register(OZONE_BLACKLIST_USERS, this::reconfOzoneBlacklistUsers)
+ .register(OZONE_BLACKLIST_GROUPS, this::reconfOzoneBlacklistGroups)
+ .register(OZONE_READ_BLACKLIST_USERS, this::reconfOzoneReadBlacklistUsers)
+ .register(OZONE_READ_BLACKLIST_GROUPS, this::reconfOzoneReadBlacklistGroups);
reconfigurationHandler.setReconfigurationCompleteCallback(reconfigurationHandler.defaultLoggingCallback());
@@ -683,9 +694,13 @@ private OzoneManager(OzoneConfiguration conf, StartupOption startupOption)
omStarterUser = UserGroupInformation.getCurrentUser().getShortUserName();
omAdmins = OzoneAdmins.getOzoneAdmins(omStarterUser, conf);
LOG.info("OM start with adminUsers: {}", omAdmins.getAdminUsernames());
+ omBlacklist = OzoneBlacklist.getOzoneBlacklist(conf);
+ LOG.info("OM start with blacklist users: {}",
+ omBlacklist.getBlacklistUsernames());
// Get read only admin list
readOnlyAdmins = OzoneAdmins.getReadonlyAdmins(conf);
+ readBlacklist = OzoneBlacklist.getReadonlyBlacklist(conf);
s3OzoneAdmins = OzoneAdmins.getS3Admins(conf);
instantiateServices(false);
@@ -4611,6 +4626,26 @@ public Collection getOmAdminGroups() {
return omAdmins.getAdminGroups();
}
+ public Collection getOmBlacklistUsernames() {
+ return omBlacklist.getBlacklistUsernames();
+ }
+
+ public Collection getOmReadOnlyBlacklistUsernames() {
+ return readBlacklist.getBlacklistUsernames();
+ }
+
+ public Collection getOmBlacklistGroups() {
+ return omBlacklist.getBlacklistGroups();
+ }
+
+ public Collection getOmReadonlyBlacklistGroups() {
+ return readBlacklist.getBlacklistGroups();
+ }
+
+ public OzoneBlacklist getOmBlacklist() {
+ return omBlacklist;
+ }
+
/**
* @param callerUgi Caller UserGroupInformation
* @return return true if the {@code ugi} is a read-only OM admin
@@ -4627,6 +4662,22 @@ public boolean isAdmin(UserGroupInformation callerUgi) {
return callerUgi != null && omAdmins.isAdmin(callerUgi);
}
+ /**
+ * @param callerUgi Caller UserGroupInformation
+ * @return returns true if the {@code ugi} is under the blacklist.
+ */
+ public boolean isBlacklisted(UserGroupInformation callerUgi) {
+ return callerUgi != null && omBlacklist.isBlacklisted(callerUgi);
+ }
+
+ /**
+ * @param callerUgi Caller UserGroupInformation
+ * @return returns true if the {@code ugi} is under the read blacklist.
+ */
+ public boolean isReadBlacklisted(UserGroupInformation callerUgi) {
+ return callerUgi != null && readBlacklist.isBlacklisted(callerUgi);
+ }
+
/**
* Check ozone admin privilege, throws exception if not admin.
* Only checks admin privilege if authorization is enabled.
@@ -5348,6 +5399,42 @@ private String reconfOzoneReadOnlyAdmins(String newVal) {
return String.valueOf(newVal);
}
+ private String reconfOzoneBlacklistUsers(String newVal) {
+ Collection blacklistUsers =
+ OzoneBlacklist.getOzoneBlacklistUsersFromConfigValue(newVal);
+ omBlacklist.setBlacklistUsernames(blacklistUsers);
+ LOG.info("Load conf {} : {}, and now blacklist users are: {}",
+ OZONE_BLACKLIST_USERS, newVal, blacklistUsers);
+ return String.valueOf(newVal);
+ }
+
+ private String reconfOzoneBlacklistGroups(String newVal) {
+ Collection blacklistGroups =
+ OzoneBlacklist.getOzoneBlacklistGroupsFromConfigValue(newVal);
+ omBlacklist.setBlacklistGroups(blacklistGroups);
+ LOG.info("Load conf {} : {}, and now blacklist groups are: {}",
+ OZONE_BLACKLIST_GROUPS, newVal, blacklistGroups);
+ return String.valueOf(newVal);
+ }
+
+ private String reconfOzoneReadBlacklistUsers(String newVal) {
+ Collection readBlacklistUsers =
+ OzoneBlacklist.getOzoneReadBlacklistUsersFromConfigValue(newVal);
+ readBlacklist.setBlacklistUsernames(readBlacklistUsers);
+ LOG.info("Load conf {} : {}, and now read blacklist users are: {}",
+ OZONE_READ_BLACKLIST_USERS, newVal, readBlacklistUsers);
+ return String.valueOf(newVal);
+ }
+
+ private String reconfOzoneReadBlacklistGroups(String newVal) {
+ Collection readBlacklistGroups =
+ OzoneBlacklist.getOzoneReadBlacklistGroupsFromConfigValue(newVal);
+ readBlacklist.setBlacklistGroups(readBlacklistGroups);
+ LOG.info("Load conf {} : {}, and now read blacklist groups are: {}",
+ OZONE_READ_BLACKLIST_GROUPS, newVal, readBlacklistGroups);
+ return String.valueOf(newVal);
+ }
+
private String reconfOzoneKeyDeletingLimitPerTask(String newVal) {
Preconditions.checkArgument(Integer.parseInt(newVal) >= 0,
OZONE_KEY_DELETING_LIMIT_PER_TASK + " cannot be negative.");
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java
index 264e6de67a73..876b71329107 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/acl/OzoneNativeAuthorizer.java
@@ -19,12 +19,14 @@
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_REQUEST;
+import com.google.common.annotations.VisibleForTesting;
import java.util.Objects;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.hdds.annotation.InterfaceStability;
import org.apache.hadoop.hdds.server.OzoneAdmins;
+import org.apache.hadoop.hdds.server.OzoneBlacklist;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.om.BucketManager;
import org.apache.hadoop.ozone.om.KeyManager;
@@ -48,6 +50,7 @@ public class OzoneNativeAuthorizer implements OzoneManagerAuthorizer {
LoggerFactory.getLogger(OzoneNativeAuthorizer.class);
private static final Predicate NO_ADMIN = any -> false;
+ private static final Predicate NO_BLACKLIST = any -> false;
private VolumeManager volumeManager;
private BucketManager bucketManager;
@@ -55,6 +58,8 @@ public class OzoneNativeAuthorizer implements OzoneManagerAuthorizer {
private PrefixManager prefixManager;
private Predicate adminCheck = NO_ADMIN;
private Predicate readOnlyAdminCheck = NO_ADMIN;
+ private Predicate blacklistCheck = NO_BLACKLIST;
+ private Predicate readOnlyBlacklistCheck = NO_BLACKLIST;
private BooleanSupplier allowListAllVolumes = () -> false;
public OzoneNativeAuthorizer() {
@@ -100,6 +105,21 @@ public boolean checkAccess(IOzoneObj ozObject, RequestContext context)
"configured to work with OzoneObjInfo type only.", INVALID_REQUEST);
}
+ if (blacklistCheck.test(context.getClientUgi())) {
+ LOG.debug("Permission DENIED on request {} for object {}. "
+ + "Reason: user is under the blacklist", context, ozObject);
+ return false;
+ }
+
+ if (readOnlyBlacklistCheck.test(context.getClientUgi())
+ && (context.getAclRights() == ACLType.READ
+ || context.getAclRights() == ACLType.READ_ACL
+ || context.getAclRights() == ACLType.LIST)) {
+ LOG.debug("Permission DENIED on request {} for object {}. "
+ + "Reason: user is under a read blacklist", context, ozObject);
+ return false;
+ }
+
// bypass all checks for admin
if (adminCheck.test(context.getClientUgi())) {
return true;
@@ -197,6 +217,8 @@ public OzoneNativeAuthorizer configure(OzoneManager om, KeyManager km, PrefixMan
allowListAllVolumes = () -> om.getConfig().isListAllVolumesAllowed();
setAdminCheck(om::isAdmin);
setReadOnlyAdminCheck(om::isReadOnlyAdmin);
+ setBlacklistCheck(om::isBlacklisted);
+ setReadOnlyBlacklistCheck(om::isReadBlacklisted);
keyManager = km;
prefixManager = pm;
@@ -211,6 +233,31 @@ public void setReadOnlyAdminCheck(Predicate check) {
readOnlyAdminCheck = Objects.requireNonNull(check, "read-only admin check");
}
+ @VisibleForTesting
+ void setOzoneAdmins(OzoneAdmins ozoneAdmins) {
+ setAdminCheck(ozoneAdmins::isAdmin);
+ }
+
+ @VisibleForTesting
+ void setOzoneBlacklist(OzoneBlacklist ozoneBlacklist) {
+ setBlacklistCheck(ozoneBlacklist::isBlacklisted);
+ }
+
+ @VisibleForTesting
+ void setOzoneReadBlacklist(OzoneBlacklist readBlacklist) {
+ setReadOnlyBlacklistCheck(readBlacklist::isBlacklisted);
+ }
+
+ public void setBlacklistCheck(Predicate check) {
+ blacklistCheck = Objects.requireNonNull(check, "blacklist check");
+ }
+
+ public void setReadOnlyBlacklistCheck(
+ Predicate check) {
+ readOnlyBlacklistCheck = Objects.requireNonNull(check,
+ "read-only blacklist check");
+ }
+
public boolean getAllowListAllVolumes() {
return allowListAllVolumes.getAsBoolean();
}
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneBlacklist.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneBlacklist.java
new file mode 100644
index 000000000000..83a97c584ebe
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/security/acl/TestOzoneBlacklist.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.ozone.security.acl;
+
+import static java.util.Arrays.asList;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collections;
+import org.apache.hadoop.hdds.server.OzoneAdmins;
+import org.apache.hadoop.hdds.server.OzoneBlacklist;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test Ozone blacklist from OzoneNativeAuthorizer.
+ */
+public class TestOzoneBlacklist {
+
+ private OzoneNativeAuthorizer nativeAuthorizer;
+
+ @BeforeEach
+ public void setup() {
+ nativeAuthorizer = new OzoneNativeAuthorizer();
+ }
+
+ @Test
+ public void testCreateVolume() throws Exception {
+ UserGroupInformation.createUserForTesting("testuser",
+ new String[]{"testgroup"});
+ try {
+ OzoneObj obj = getTestVolumeobj("testvolume");
+ RequestContext context = getUserRequestContext("testuser",
+ IAccessAuthorizer.ACLType.CREATE);
+ testBlacklistedUserOperations(obj, context);
+ testBlacklistedGroupOperations(obj, context);
+ } finally {
+ UserGroupInformation.reset();
+ }
+ }
+
+ @Test
+ public void testBucketOperation() throws OMException {
+ UserGroupInformation.createUserForTesting("testuser",
+ new String[]{"testgroup"});
+ try {
+ OzoneObj obj = getTestBucketobj("testbucket");
+ RequestContext context = getUserRequestContext("testuser",
+ IAccessAuthorizer.ACLType.LIST);
+ nativeAuthorizer.setOzoneReadBlacklist(new OzoneBlacklist(
+ Collections.singletonList("testuser"), null));
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching read only blacklisted users are not allowed to"
+ + " perform read operations");
+
+ context = getUserRequestContext("testuser",
+ IAccessAuthorizer.ACLType.READ);
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching read blacklisted users are not allowed to"
+ + " perform read operations");
+
+ context = getUserRequestContext("testuser",
+ IAccessAuthorizer.ACLType.READ_ACL);
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching read blacklisted users are not allowed to"
+ + " perform read operations");
+
+ context = getUserRequestContext("testuser",
+ IAccessAuthorizer.ACLType.WRITE);
+ RequestContext finalContext = context;
+ assertThrows(NullPointerException.class,
+ () -> nativeAuthorizer.checkAccess(obj, finalContext));
+
+ nativeAuthorizer.setOzoneReadBlacklist(new OzoneBlacklist(
+ null, Collections.singletonList("testgroup")));
+ context = getUserRequestContext("testuser",
+ IAccessAuthorizer.ACLType.READ_ACL);
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching blacklisted read only users are not allowed to"
+ + " perform read operations");
+ } finally {
+ UserGroupInformation.reset();
+ }
+ }
+
+ @Test
+ public void testListAllVolume() throws Exception {
+ UserGroupInformation.createUserForTesting("testuser",
+ new String[]{"testgroup"});
+ try {
+ OzoneObj obj = getTestVolumeobj("/");
+ RequestContext context = getUserRequestContext("testuser",
+ IAccessAuthorizer.ACLType.LIST);
+ testBlacklistedUserOperations(obj, context);
+ testBlacklistedGroupOperations(obj, context);
+ } finally {
+ UserGroupInformation.reset();
+ }
+ }
+
+ private void testBlacklistedUserOperations(
+ OzoneObj obj, RequestContext context) throws OMException {
+ nativeAuthorizer.setOzoneAdmins(new OzoneAdmins(
+ Collections.singletonList("testuser")));
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "admins are allowed to perform any operations");
+
+ nativeAuthorizer.setOzoneBlacklist(
+ new OzoneBlacklist(Collections.singletonList("testuser")));
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching blacklisted users are not allowed to perform"
+ + " any operations");
+
+ nativeAuthorizer.setOzoneBlacklist(
+ new OzoneBlacklist(asList("testuser2", "testuser")));
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching blacklisted users are not allowed to perform"
+ + " any operations");
+
+ nativeAuthorizer.setOzoneBlacklist(
+ new OzoneBlacklist(asList("testuser2", "testuser3")));
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "non-blacklisted users should be allowed to perform"
+ + " operations");
+
+ nativeAuthorizer.setOzoneReadBlacklist(new OzoneBlacklist(
+ Collections.singletonList("testuser"), null));
+ if (context.getAclRights() == IAccessAuthorizer.ACLType.LIST
+ || context.getAclRights() == IAccessAuthorizer.ACLType.READ
+ || context.getAclRights() == IAccessAuthorizer.ACLType.READ_ACL) {
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching read blacklisted users are not allowed to perform"
+ + " read operations");
+ } else {
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "non-matched read blacklisted users should still be able"
+ + " to perform operations");
+ }
+
+ nativeAuthorizer.setOzoneReadBlacklist(new OzoneBlacklist(
+ Collections.singletonList("testuser1"), null));
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "admins are allowed to perform any operations");
+ }
+
+ private void testBlacklistedGroupOperations(
+ OzoneObj obj, RequestContext context) throws Exception {
+ nativeAuthorizer.setOzoneAdmins(new OzoneAdmins(null,
+ asList("testgroup", "anothergroup")));
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "users in admin groups are allowed to perform any operations");
+
+ nativeAuthorizer.setOzoneBlacklist(
+ new OzoneBlacklist(null,
+ Collections.singletonList("testgroup")));
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "users in matching blacklisted groups are not allowed to"
+ + " perform any operations");
+
+ nativeAuthorizer.setOzoneBlacklist(
+ new OzoneBlacklist(null,
+ asList("testgroup", "anothergroup2")));
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "users in matching blacklisted groups are not allowed to"
+ + " perform any operations");
+
+ nativeAuthorizer.setOzoneBlacklist(
+ new OzoneBlacklist(null,
+ asList("testgroup2", "testgroup3")));
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "users in admin groups and not in blacklist groups are"
+ + " allowed to perform any operations");
+
+ nativeAuthorizer.setOzoneReadBlacklist(new OzoneBlacklist(null,
+ Collections.singletonList("testgroup")));
+ if (context.getAclRights() == IAccessAuthorizer.ACLType.LIST
+ || context.getAclRights() == IAccessAuthorizer.ACLType.READ
+ || context.getAclRights() == IAccessAuthorizer.ACLType.READ_ACL) {
+ assertFalse(nativeAuthorizer.checkAccess(obj, context),
+ "matching read blacklisted users are not allowed to"
+ + " perform read operations");
+ } else {
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "non-matched read blacklisted users should still be"
+ + " able to perform operations");
+ }
+
+ nativeAuthorizer.setOzoneReadBlacklist(new OzoneBlacklist(null,
+ Collections.singletonList("testgroup1")));
+ assertTrue(nativeAuthorizer.checkAccess(obj, context),
+ "users in admin groups and not in blacklist groups are"
+ + " allowed to perform any operations");
+ }
+
+ private RequestContext getUserRequestContext(String username,
+ IAccessAuthorizer.ACLType type) {
+ return RequestContext.newBuilder()
+ .setClientUgi(UserGroupInformation.createRemoteUser(username))
+ .setAclType(IAccessAuthorizer.ACLIdentityType.USER)
+ .setAclRights(type)
+ .build();
+ }
+
+ private OzoneObj getTestVolumeobj(String volumename) {
+ return OzoneObjInfo.Builder.newBuilder()
+ .setResType(OzoneObj.ResourceType.VOLUME)
+ .setStoreType(OzoneObj.StoreType.OZONE)
+ .setVolumeName(volumename).build();
+ }
+
+ private OzoneObj getTestBucketobj(String bucketname) {
+ return OzoneObjInfo.Builder.newBuilder()
+ .setResType(OzoneObj.ResourceType.BUCKET)
+ .setStoreType(OzoneObj.StoreType.OZONE)
+ .setVolumeName(bucketname).build();
+ }
+}