You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by si...@apache.org on 2022/05/17 21:36:25 UTC

[ozone] branch HDDS-4944 updated: HDDS-6612. [Multi-Tenant] Add a config key to enable or disable S3 Multi-Tenancy feature (#3397)

This is an automated email from the ASF dual-hosted git repository.

siyao pushed a commit to branch HDDS-4944
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/HDDS-4944 by this push:
     new c55bd0290f HDDS-6612. [Multi-Tenant] Add a config key to enable or disable S3 Multi-Tenancy feature (#3397)
c55bd0290f is described below

commit c55bd0290fdc070577693cb89ec6a46113e65661
Author: Siyao Meng <50...@users.noreply.github.com>
AuthorDate: Tue May 17 14:36:20 2022 -0700

    HDDS-6612. [Multi-Tenant] Add a config key to enable or disable S3 Multi-Tenancy feature (#3397)
---
 .../common/src/main/resources/ozone-default.xml    |   8 +
 .../docs/content/feature/S3-Multi-Tenancy-Setup.md |   6 +-
 .../org/apache/hadoop/ozone/om/OMConfigKeys.java   |   4 +
 .../hadoop/ozone/om/exceptions/OMException.java    |   4 +-
 ...OzoneManagerProtocolClientSideTranslatorPB.java | 102 ++++++------
 .../src/main/compose/ozonesecure/docker-config     |   4 +-
 .../om/multitenant/TestMultiTenantVolume.java      |   2 +
 .../hadoop/ozone/shell/TestOzoneTenantShell.java   |   4 +
 .../src/main/proto/OmClientProtocol.proto          |   2 +
 .../hadoop/ozone/om/OMMultiTenantManager.java      |  88 ++++++++++-
 .../hadoop/ozone/om/OMMultiTenantManagerImpl.java  |   9 +-
 .../org/apache/hadoop/ozone/om/OzoneAclUtils.java  |   7 +-
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |  42 ++++-
 .../om/ratis/utils/OzoneManagerRatisUtils.java     |   6 +
 .../s3/tenant/OMTenantAssignAdminRequest.java      |  16 +-
 .../tenant/OMTenantAssignUserAccessIdRequest.java  |  20 +--
 .../request/s3/tenant/OMTenantCreateRequest.java   |  17 +-
 .../request/s3/tenant/OMTenantDeleteRequest.java   |   2 +-
 .../s3/tenant/OMTenantRevokeAdminRequest.java      |  29 ++--
 .../tenant/OMTenantRevokeUserAccessIdRequest.java  |  15 +-
 .../protocolPB/OzoneManagerRequestHandler.java     |   3 +
 .../hadoop/ozone/om/TestOMMultiTenantManager.java  | 175 +++++++++++++++++++++
 .../ozone/om/request/OMRequestTestUtils.java       | 154 ++++++++++++++++++
 .../s3/security/TestS3GetSecretRequest.java        |   2 -
 24 files changed, 593 insertions(+), 128 deletions(-)

diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 703e3c5c7b..b95f858834 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -3118,4 +3118,12 @@
       log level is debug. Ex: "CREATE_CONTAINER,READ_CONTAINER,UPDATE_CONTAINER".
     </description>
   </property>
+
+  <property>
+    <name>ozone.om.multitenancy.enabled</name>
+    <value>false</value>
+    <tag>OZONE, OM</tag>
+    <description>Enable S3 Multi-Tenancy. If disabled, all S3 multi-tenancy requests are rejected.
+    </description>
+  </property>
 </configuration>
diff --git a/hadoop-hdds/docs/content/feature/S3-Multi-Tenancy-Setup.md b/hadoop-hdds/docs/content/feature/S3-Multi-Tenancy-Setup.md
index d6f77e7edf..79e1320045 100644
--- a/hadoop-hdds/docs/content/feature/S3-Multi-Tenancy-Setup.md
+++ b/hadoop-hdds/docs/content/feature/S3-Multi-Tenancy-Setup.md
@@ -42,9 +42,13 @@ Follow [this guide]({{< ref "interface/S3.md" >}}) the cluster to set up at leas
 First make sure ACL is enabled, and `RangerOzoneAuthorizer` is the effective ACL authorizer implementation in-use for Ozone.
 If that is not the case, [follow this]({{< ref "security/SecurityWithRanger.md" >}}). 
 
-Then simply add the following configs to `ozone-site.xml`:
+Then add the following configs to all Ozone Managers' `ozone-site.xml`:
 
 ```xml
+<property>
+   <name>ozone.om.multitenancy.enabled</name>
+   <value>true</value>
+</property>
 <property>
 	<name>ozone.om.ranger.https-address</name>
 	<value>https://RANGER_HOSTNAME:6182</value>
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
index 13afb39743..f14b7f6239 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
@@ -312,6 +312,10 @@ public final class OMConfigKeys {
   public static final int OZONE_OM_UNFLUSHED_TRANSACTION_MAX_COUNT_DEFAULT
       = 10000;
 
+  public static final String OZONE_OM_MULTITENANCY_ENABLED =
+      "ozone.om.multitenancy.enabled";
+  public static final boolean OZONE_OM_MULTITENANCY_ENABLED_DEFAULT = false;
+
   /**
    * Temporary configuration properties for Ranger REST use in multitenancy.
    */
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
index f2838f5b51..717df88b48 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
@@ -254,6 +254,8 @@ public class OMException extends IOException {
     TENANT_AUTHORIZER_ERROR,
 
     VOLUME_IS_REFERENCED,
-    TENANT_NOT_EMPTY
+    TENANT_NOT_EMPTY,
+
+    FEATURE_NOT_ENABLED
   }
 }
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
index 6248f27be0..2e77ccd7f5 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
@@ -995,25 +995,28 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
    */
   @Override
   public void createTenant(OmTenantArgs omTenantArgs) throws IOException {
-    final CreateTenantRequest request = CreateTenantRequest.newBuilder()
-        .setTenantId(omTenantArgs.getTenantId())
-        .setVolumeName(omTenantArgs.getVolumeName())
-        // TODO: Add more args like policy names later
-        .build();
+    final CreateTenantRequest.Builder requestBuilder =
+        CreateTenantRequest.newBuilder()
+            .setTenantId(omTenantArgs.getTenantId())
+            .setVolumeName(omTenantArgs.getVolumeName());
+            // Can add more args (like policy names) later if needed
     final OMRequest omRequest = createOMRequest(Type.CreateTenant)
-        .setCreateTenantRequest(request)
+        .setCreateTenantRequest(requestBuilder)
         .build();
     final OMResponse omResponse = submitRequest(omRequest);
     handleError(omResponse);
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public DeleteTenantState deleteTenant(String tenantId) throws IOException {
-    final DeleteTenantRequest request = DeleteTenantRequest.newBuilder()
-        .setTenantId(tenantId)
-        .build();
+    final DeleteTenantRequest.Builder requestBuilder =
+        DeleteTenantRequest.newBuilder()
+            .setTenantId(tenantId);
     final OMRequest omRequest = createOMRequest(Type.DeleteTenant)
-        .setDeleteTenantRequest(request)
+        .setDeleteTenantRequest(requestBuilder)
         .build();
     final OMResponse omResponse = submitRequest(omRequest);
     final DeleteTenantResponse resp =
@@ -1030,14 +1033,13 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
   public S3SecretValue tenantAssignUserAccessId(
       String username, String tenantId, String accessId) throws IOException {
 
-    final TenantAssignUserAccessIdRequest request =
+    final TenantAssignUserAccessIdRequest.Builder requestBuilder =
         TenantAssignUserAccessIdRequest.newBuilder()
-        .setUserPrincipal(username)
-        .setTenantId(tenantId)
-        .setAccessId(accessId)
-        .build();
+            .setUserPrincipal(username)
+            .setTenantId(tenantId)
+            .setAccessId(accessId);
     final OMRequest omRequest = createOMRequest(Type.TenantAssignUserAccessId)
-        .setTenantAssignUserAccessIdRequest(request)
+        .setTenantAssignUserAccessIdRequest(requestBuilder)
         .build();
     final OMResponse omResponse = submitRequest(omRequest);
     final TenantAssignUserAccessIdResponse resp = handleError(omResponse)
@@ -1053,12 +1055,11 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
   public void tenantRevokeUserAccessId(String accessId)
       throws IOException {
 
-    final TenantRevokeUserAccessIdRequest request =
+    final TenantRevokeUserAccessIdRequest.Builder requestBuilder =
         TenantRevokeUserAccessIdRequest.newBuilder()
-            .setAccessId(accessId)
-            .build();
+            .setAccessId(accessId);
     final OMRequest omRequest = createOMRequest(Type.TenantRevokeUserAccessId)
-        .setTenantRevokeUserAccessIdRequest(request)
+        .setTenantRevokeUserAccessIdRequest(requestBuilder)
         .build();
     final OMResponse omResponse = submitRequest(omRequest);
     handleError(omResponse);
@@ -1073,8 +1074,8 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
 
     final TenantAssignAdminRequest.Builder requestBuilder =
         TenantAssignAdminRequest.newBuilder()
-        .setAccessId(accessId)
-        .setDelegated(delegated);
+            .setAccessId(accessId)
+            .setDelegated(delegated);
     if (tenantId != null) {
       requestBuilder.setTenantId(tenantId);
     }
@@ -1114,12 +1115,11 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
   public TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
       throws IOException {
 
-    final TenantGetUserInfoRequest request =
+    final TenantGetUserInfoRequest.Builder requestBuilder =
         TenantGetUserInfoRequest.newBuilder()
-            .setUserPrincipal(userPrincipal)
-            .build();
+            .setUserPrincipal(userPrincipal);
     final OMRequest omRequest = createOMRequest(Type.TenantGetUserInfo)
-        .setTenantGetUserInfoRequest(request)
+        .setTenantGetUserInfoRequest(requestBuilder)
         .build();
     final OMResponse omResponse = submitRequest(omRequest);
     final TenantGetUserInfoResponse resp = handleError(omResponse)
@@ -1128,15 +1128,20 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
     return TenantUserInfoValue.fromProtobuf(resp);
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public TenantUserList listUsersInTenant(String tenantId, String prefix)
       throws IOException {
-    TenantListUserRequest.Builder builder =
-        TenantListUserRequest.newBuilder().setTenantId(tenantId);
+
+    final TenantListUserRequest.Builder requestBuilder =
+        TenantListUserRequest.newBuilder()
+            .setTenantId(tenantId);
     if (prefix != null) {
-      builder.setPrefix(prefix);
+      requestBuilder.setPrefix(prefix);
     }
-    TenantListUserRequest request = builder.build();
+    TenantListUserRequest request = requestBuilder.build();
 
     final OMRequest omRequest = createOMRequest(Type.TenantListUser)
         .setTenantListUserRequest(request).build();
@@ -1146,30 +1151,16 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
     return TenantUserList.fromProtobuf(resp);
   }
 
-  @Override
-  public S3VolumeContext getS3VolumeContext() throws IOException {
-    final GetS3VolumeContextRequest request = GetS3VolumeContextRequest
-        .newBuilder()
-        .build();
-    final OMRequest omRequest = createOMRequest(Type.GetS3VolumeContext)
-        .setGetS3VolumeContextRequest(request)
-        .build();
-    final OMResponse omResponse = submitRequest(omRequest);
-    final GetS3VolumeContextResponse resp =
-        handleError(omResponse).getGetS3VolumeContextResponse();
-    return S3VolumeContext.fromProtobuf(resp);
-  }
-
   /**
    * {@inheritDoc}
    */
   @Override
   public TenantStateList listTenant() throws IOException {
 
-    final ListTenantRequest request = ListTenantRequest.newBuilder()
-        .build();
+    final ListTenantRequest.Builder requestBuilder =
+        ListTenantRequest.newBuilder();
     final OMRequest omRequest = createOMRequest(Type.ListTenant)
-        .setListTenantRequest(request)
+        .setListTenantRequest(requestBuilder)
         .build();
     final OMResponse omResponse = submitRequest(omRequest);
     final ListTenantResponse resp = handleError(omResponse)
@@ -1178,6 +1169,23 @@ public final class OzoneManagerProtocolClientSideTranslatorPB
     return TenantStateList.fromProtobuf(resp.getTenantStateList());
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public S3VolumeContext getS3VolumeContext() throws IOException {
+
+    final GetS3VolumeContextRequest.Builder requestBuilder =
+        GetS3VolumeContextRequest.newBuilder();
+    final OMRequest omRequest = createOMRequest(Type.GetS3VolumeContext)
+        .setGetS3VolumeContextRequest(requestBuilder)
+        .build();
+    final OMResponse omResponse = submitRequest(omRequest);
+    final GetS3VolumeContextResponse resp =
+        handleError(omResponse).getGetS3VolumeContextResponse();
+    return S3VolumeContext.fromProtobuf(resp);
+  }
+
   /**
    * Return the proxy object underlying this protocol translator.
    *
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
index 7d916c98b5..e58b7512e4 100644
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
@@ -133,4 +133,6 @@ OZONE_LOG_DIR=/var/log/hadoop
 no_proxy=om,scm,recon,s3g,kdc,localhost,127.0.0.1
 
 OZONE-SITE.XML_ozone.om.ranger.https-address=https://ranger:6182
-
+OZONE-SITE.XML_ozone.om.multitenancy.enabled=true
+OZONE-SITE.XML_ozone.om.ranger.https.admin.api.user=admin
+OZONE-SITE.XML_ozone.om.ranger.https.admin.api.passwd=passwd
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
index 138ad57470..f2c8567296 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java
@@ -45,6 +45,7 @@ import java.util.concurrent.TimeoutException;
 
 import static org.apache.hadoop.ozone.admin.scm.FinalizeUpgradeCommandUtil.isDone;
 import static org.apache.hadoop.ozone.admin.scm.FinalizeUpgradeCommandUtil.isStarting;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED;
 
 /**
  * Tests that S3 requests for a tenant are directed to that tenant's volume,
@@ -65,6 +66,7 @@ public class TestMultiTenantVolume {
     OzoneConfiguration conf = new OzoneConfiguration();
     conf.setBoolean(
         OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER, true);
+    conf.setBoolean(OZONE_OM_MULTITENANCY_ENABLED, true);
     MiniOzoneCluster.Builder builder = MiniOzoneCluster.newBuilder(conf)
         .withoutDatanodes()
         .setOmLayoutVersion(OMLayoutFeature.INITIAL_VERSION.layoutVersion());
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
index 0db0c69e45..39bdbaae54 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
@@ -59,6 +59,7 @@ import java.util.List;
 import java.util.UUID;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED;
 import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD;
 import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_USER;
 import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_HTTPS_ADDRESS_KEY;
@@ -122,6 +123,9 @@ public class TestOzoneTenantShell {
     }
 
     conf = new OzoneConfiguration();
+    conf.setBoolean(
+        OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER, true);
+    conf.setBoolean(OZONE_OM_MULTITENANCY_ENABLED, true);
 
     if (USE_ACTUAL_RANGER) {
       conf.set(OZONE_RANGER_HTTPS_ADDRESS_KEY, System.getenv("RANGER_ADDRESS"));
diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index ca3f0bd975..750ece9573 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -418,6 +418,8 @@ enum Status {
 
     VOLUME_IS_REFERENCED = 83;
     TENANT_NOT_EMPTY = 84;
+
+    FEATURE_NOT_ENABLED = 85;
 }
 
 /**
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
index 3554ab6f29..6d2d2ab0d4 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManager.java
@@ -19,12 +19,25 @@ package org.apache.hadoop.ozone.om;
 import java.io.IOException;
 
 import com.google.common.base.Optional;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.OzoneConsts;
 import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
 import org.apache.http.auth.BasicUserPrincipal;
+import org.slf4j.Logger;
+
+import static org.apache.hadoop.ozone.OzoneConsts.TENANT_ID_USERNAME_DELIMITER;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_USER;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_HTTPS_ADDRESS_KEY;
+import static org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER;
 
 /**
  * OM MultiTenant manager interface.
@@ -117,7 +130,9 @@ public interface OMMultiTenantManager {
    * @param userPrincipal user name
    * @return access ID in the form of tenantName$username
    */
-  String getDefaultAccessId(String tenantId, String userPrincipal);
+  static String getDefaultAccessId(String tenantId, String userPrincipal) {
+    return tenantId + TENANT_ID_USERNAME_DELIMITER + userPrincipal;
+  }
 
   /**
    * Returns true if user is the tenant's admin or Ozone admin, false otherwise.
@@ -235,4 +250,75 @@ public interface OMMultiTenantManager {
    * @throws IOException
    */
   boolean isTenantEmpty(String tenantId) throws IOException;
+
+  /**
+   * Returns true if Multi-Tenancy can be successfully enabled given the OM
+   * instance and conf; returns false if ozone.om.multitenancy.enabled = false
+   *
+   * Config validation will be performed on conf if the intent to enable
+   * Multi-Tenancy is specified (i.e. ozone.om.multitenancy.enabled = true),
+   * if the validation failed, an exception will be thrown to prevent OM from
+   * starting up.
+   */
+  static boolean checkAndEnableMultiTenancy(
+      OzoneManager ozoneManager, OzoneConfiguration conf) {
+
+    // Borrow the logger from OM instance
+    final Logger logger = OzoneManager.LOG;
+
+    boolean isS3MultiTenancyEnabled = conf.getBoolean(
+        OZONE_OM_MULTITENANCY_ENABLED, OZONE_OM_MULTITENANCY_ENABLED_DEFAULT);
+
+    final boolean devSkipMTCheck = conf.getBoolean(
+        OZONE_OM_TENANT_DEV_SKIP_RANGER, false);
+
+    // If ozone.om.multitenancy.enabled = false, skip the validation
+    // Or if dev skip check flag is set, skip the validation (used in UT)
+    if (!isS3MultiTenancyEnabled || devSkipMTCheck) {
+      return isS3MultiTenancyEnabled;
+    }
+
+    // Validate configs required to enable S3 multi-tenancy
+    if (!ozoneManager.isSecurityEnabled()) {
+      isS3MultiTenancyEnabled = false;
+      logger.error("Ozone security is required to enable S3 Multi-Tenancy");
+    } else if (!SecurityUtil.getAuthenticationMethod(conf).equals(
+        AuthenticationMethod.KERBEROS)) {
+      isS3MultiTenancyEnabled = false;
+      logger.error("Kerberos authentication is required to enable S3 "
+          + "Multi-Tenancy");
+    }
+
+    // TODO: Validate accessAuthorizer later. We can't do that for now:
+    //  1. Tenant acceptance test env (ozonesecure) uses OzoneNativeAuthorizer
+    //  2. RangerOzoneAuthorizer is external class
+
+    final String rangerAddress = conf.get(OZONE_RANGER_HTTPS_ADDRESS_KEY);
+    if (StringUtils.isBlank(rangerAddress)) {
+      isS3MultiTenancyEnabled = false;
+      logger.error("{} is required to enable S3 Multi-Tenancy but not set",
+          OZONE_RANGER_HTTPS_ADDRESS_KEY);
+    }
+
+    // TODO: Do not check Ranger user/passwd if Ranger Java client is enabled
+    final String rangerUser = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER);
+    if (StringUtils.isBlank(rangerUser)) {
+      isS3MultiTenancyEnabled = false;
+      logger.error("{} is required to enable S3 Multi-Tenancy but not set",
+          OZONE_OM_RANGER_HTTPS_ADMIN_API_USER);
+    }
+    final String rangerPw = conf.get(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD);
+    if (StringUtils.isBlank(rangerPw)) {
+      isS3MultiTenancyEnabled = false;
+      logger.error("{} is required to enable S3 Multi-Tenancy but not set",
+          OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD);
+    }
+
+    if (!isS3MultiTenancyEnabled) {
+      throw new RuntimeException("Failed to meet one or more requirements to "
+          + "enable S3 Multi-Tenancy");
+    }
+
+    return true;
+  }
 }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
index 0bc54a6ae4..1c359be888 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
@@ -17,7 +17,6 @@
  */
 package org.apache.hadoop.ozone.om;
 
-import static org.apache.hadoop.ozone.OzoneConsts.TENANT_ID_USERNAME_DELIMITER;
 import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_ACCESS_ID;
 import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TENANT_AUTHORIZER_ERROR;
 import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TENANT_NOT_FOUND;
@@ -84,8 +83,8 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
   private static final Logger LOG =
       LoggerFactory.getLogger(OMMultiTenantManagerImpl.class);
 
-  // TODO: Remove when proper testing infra is deployed.
-  // Internal dev flag to skip Ranger communication.
+  // Internal flag to skip Ranger communication,
+  // and to skip Ozone config validation for S3 multi-tenancy
   public static final String OZONE_OM_TENANT_DEV_SKIP_RANGER =
       "ozone.om.tenant.dev.skip.ranger";
 
@@ -349,10 +348,6 @@ public class OMMultiTenantManagerImpl implements OMMultiTenantManager {
     return null;
   }
 
-  public String getDefaultAccessId(String tenantId, String userPrincipal) {
-    return tenantId + TENANT_ID_USERNAME_DELIMITER + userPrincipal;
-  }
-
   /**
    * {@inheritDoc}
    */
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneAclUtils.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneAclUtils.java
index 0aab387ea4..b6eaeca0ac 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneAclUtils.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneAclUtils.java
@@ -47,8 +47,11 @@ public final class OzoneAclUtils {
    * If the access ID does not belong to a tenant, the access ID is returned
    * as is to be used as the principal.
    */
-  public static String accessIdToUserPrincipal(String accessID)
-      throws IOException {
+  public static String accessIdToUserPrincipal(String accessID) {
+    if (multiTenantManager == null) {
+      return accessID;
+    }
+
     String principal = multiTenantManager.getUserNameGivenAccessId(accessID);
     if (principal == null) {
       principal = accessID;
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 47a2282d87..c8e1ed5a29 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
@@ -372,6 +372,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
   private final boolean useRatisForReplication;
   private final String defaultBucketLayout;
 
+  private boolean isS3MultiTenancyEnabled;
+
   private boolean isNativeAuthorizerEnabled;
 
   private ExitManager exitManager;
@@ -534,6 +536,10 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
       blockTokenMgr = createBlockTokenSecretManager(configuration);
     }
 
+    // Enable S3 multi-tenancy if config keys are set
+    this.isS3MultiTenancyEnabled =
+        OMMultiTenantManager.checkAndEnableMultiTenancy(this, conf);
+
     // Get admin list
     omAdminUsernames = getOzoneAdminsFromConfig(configuration);
     instantiateServices(false);
@@ -653,8 +659,12 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
   private void instantiateServices(boolean withNewSnapshot) throws IOException {
 
     metadataManager = new OmMetadataManagerImpl(configuration);
-    multiTenantManager = new OMMultiTenantManagerImpl(this, configuration);
-    OzoneAclUtils.setOMMultiTenantManager(multiTenantManager);
+    LOG.info("S3 Multi-Tenancy is {}",
+        isS3MultiTenancyEnabled ? "enabled" : "disabled");
+    if (isS3MultiTenancyEnabled) {
+      multiTenantManager = new OMMultiTenantManagerImpl(this, configuration);
+      OzoneAclUtils.setOMMultiTenantManager(multiTenantManager);
+    }
     volumeManager = new VolumeManagerImpl(metadataManager, configuration);
     bucketManager = new BucketManagerImpl(metadataManager, getKmsProvider(),
         isRatisEnabled);
@@ -756,6 +766,26 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
     return grpcBlockTokenEnabled;
   }
 
+  /**
+   * Returns true if S3 multi-tenancy is enabled; false otherwise.
+   */
+  public boolean isS3MultiTenancyEnabled() {
+    return isS3MultiTenancyEnabled;
+  }
+
+  /**
+   * Throws OMException FEATURE_NOT_ENABLED if S3 multi-tenancy is not enabled.
+   */
+  public void checkS3MultiTenancyEnabled() throws OMException {
+    if (isS3MultiTenancyEnabled()) {
+      return;
+    }
+
+    throw new OMException("S3 multi-tenancy feature is not enabled. Please "
+        + "set ozone.om.multitenancy.enabled to true and restart all OMs.",
+        ResultCodes.FEATURE_NOT_ENABLED);
+  }
+
   /**
    * Return config value of {@link OzoneConfigKeys#OZONE_SECURITY_ENABLED_KEY}.
    */
@@ -3133,8 +3163,10 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
       }
     } else {
       String accessId = s3Auth.getAccessId();
-      Optional<String> optionalTenantId =
-          multiTenantManager.getTenantForAccessID(accessId);
+      // If S3 Multi-Tenancy is not enabled, all S3 requests will be redirected
+      // to the default s3v for compatibility
+      final Optional<String> optionalTenantId = isS3MultiTenancyEnabled() ?
+          multiTenantManager.getTenantForAccessID(accessId) : Optional.absent();
 
       if (!optionalTenantId.isPresent()) {
         final UserGroupInformation s3gUGI =
@@ -3150,6 +3182,8 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
               + "requests to default s3 volume {}.", accessId, s3Volume);
         }
       } else {
+        // S3 Multi-Tenancy is enabled, and the accessId is assigned to a tenant
+
         final String tenantId = optionalTenantId.get();
 
         OmDBTenantState tenantState =
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
index b425ea4424..09b92a22fe 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
@@ -190,16 +190,22 @@ public final class OzoneManagerRatisUtils {
     case PurgePaths:
       return new OMPathsPurgeRequestWithFSO(omRequest);
     case CreateTenant:
+      ozoneManager.checkS3MultiTenancyEnabled();
       return new OMTenantCreateRequest(omRequest);
     case DeleteTenant:
+      ozoneManager.checkS3MultiTenancyEnabled();
       return new OMTenantDeleteRequest(omRequest);
     case TenantAssignUserAccessId:
+      ozoneManager.checkS3MultiTenancyEnabled();
       return new OMTenantAssignUserAccessIdRequest(omRequest);
     case TenantRevokeUserAccessId:
+      ozoneManager.checkS3MultiTenancyEnabled();
       return new OMTenantRevokeUserAccessIdRequest(omRequest);
     case TenantAssignAdmin:
+      ozoneManager.checkS3MultiTenancyEnabled();
       return new OMTenantAssignAdminRequest(omRequest);
     case TenantRevokeAdmin:
+      ozoneManager.checkS3MultiTenancyEnabled();
       return new OMTenantRevokeAdminRequest(omRequest);
 
     /*
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java
index f9a6a9e467..d301ca963c 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java
@@ -74,8 +74,11 @@ public class OMTenantAssignAdminRequest extends OMClientRequest {
   @Override
   @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA)
   public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+
+
+    final OMRequest omRequest = super.preExecute(ozoneManager);
     final TenantAssignAdminRequest request =
-        getOmRequest().getTenantAssignAdminRequest();
+        omRequest.getTenantAssignAdminRequest();
 
     final String accessId = request.getAccessId();
     String tenantId = request.getTenantId();
@@ -124,20 +127,13 @@ public class OMTenantAssignAdminRequest extends OMClientRequest {
     ozoneManager.getMultiTenantManager().assignTenantAdmin(
         request.getAccessId(), delegated);
 
-    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
-        .setUserInfo(getUserInfo())
+    final OMRequest.Builder omRequestBuilder = omRequest.toBuilder()
         .setTenantAssignAdminRequest(
             TenantAssignAdminRequest.newBuilder()
                 .setAccessId(accessId)
                 .setTenantId(tenantId)
                 .setDelegated(delegated)
-                .build())
-        .setCmdType(getOmRequest().getCmdType())
-        .setClientId(getOmRequest().getClientId());
-
-    if (getOmRequest().hasTraceID()) {
-      omRequestBuilder.setTraceID(getOmRequest().getTraceID());
-    }
+                .build());
 
     return omRequestBuilder.build();
   }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignUserAccessIdRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignUserAccessIdRequest.java
index 509d5de6ce..053b766168 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignUserAccessIdRequest.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignUserAccessIdRequest.java
@@ -28,6 +28,7 @@ import org.apache.hadoop.ozone.OzoneConsts;
 import org.apache.hadoop.ozone.audit.OMAction;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.om.OMMetrics;
+import org.apache.hadoop.ozone.om.OMMultiTenantManager;
 import org.apache.hadoop.ozone.om.OzoneManager;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
@@ -107,8 +108,10 @@ public class OMTenantAssignUserAccessIdRequest extends OMClientRequest {
   @Override
   @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA)
   public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+
+    final OMRequest omRequest = super.preExecute(ozoneManager);
     final TenantAssignUserAccessIdRequest request =
-        getOmRequest().getTenantAssignUserAccessIdRequest();
+        omRequest.getTenantAssignUserAccessIdRequest();
 
     final String tenantId = request.getTenantId();
 
@@ -133,8 +136,8 @@ public class OMTenantAssignUserAccessIdRequest extends OMClientRequest {
     }
 
     // HDDS-6366: Disallow specifying custom accessId.
-    final String expectedAccessId = ozoneManager.getMultiTenantManager()
-        .getDefaultAccessId(tenantId, userPrincipal);
+    final String expectedAccessId =
+        OMMultiTenantManager.getDefaultAccessId(tenantId, userPrincipal);
     if (!accessId.equals(expectedAccessId)) {
       throw new OMException("Invalid accessId '" + accessId + "'. "
           + "Specifying a custom access ID disallowed. "
@@ -166,15 +169,8 @@ public class OMTenantAssignUserAccessIdRequest extends OMClientRequest {
             .setAwsSecret(s3Secret)
             .setKerberosID(accessId).build();
 
-    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
-        .setUserInfo(getUserInfo())
-        .setUpdateGetS3SecretRequest(updateGetS3SecretRequest)
-        .setCmdType(getOmRequest().getCmdType())
-        .setClientId(getOmRequest().getClientId());
-
-    if (getOmRequest().hasTraceID()) {
-      omRequestBuilder.setTraceID(getOmRequest().getTraceID());
-    }
+    final OMRequest.Builder omRequestBuilder = omRequest.toBuilder()
+        .setUpdateGetS3SecretRequest(updateGetS3SecretRequest);
 
     return omRequestBuilder.build();
   }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
index 27e25be6fa..b87e76a6e5 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
@@ -118,7 +118,9 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
     // Check Ozone cluster admin privilege
     ozoneManager.getMultiTenantManager().checkAdmin();
 
-    final CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
+    final OMRequest omRequest = super.preExecute(ozoneManager);
+    final CreateTenantRequest request = omRequest.getCreateTenantRequest();
+    Preconditions.checkNotNull(request);
     final String tenantId = request.getTenantId();
 
     // Check tenantId validity
@@ -169,21 +171,14 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
     tenantInContext = ozoneManager.getMultiTenantManager()
         .createTenantAccessInAuthorizer(tenantId);
 
-    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
+    final OMRequest.Builder omRequestBuilder = omRequest.toBuilder()
         .setCreateTenantRequest(
             CreateTenantRequest.newBuilder()
                 .setTenantId(tenantId)
                 .setVolumeName(volumeName))
         .setCreateVolumeRequest(
-            CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
-        // TODO: Can the three lines below be ignored?
-        .setUserInfo(getUserInfo())
-        .setCmdType(getOmRequest().getCmdType())
-        .setClientId(getOmRequest().getClientId());
-
-    if (getOmRequest().hasTraceID()) {
-      omRequestBuilder.setTraceID(getOmRequest().getTraceID());
-    }
+            CreateVolumeRequest.newBuilder()
+                .setVolumeInfo(updatedVolumeInfo));
 
     return omRequestBuilder.build();
   }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java
index 9e6b146cb0..8bedef485d 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java
@@ -74,7 +74,7 @@ public class OMTenantDeleteRequest extends OMVolumeRequest {
 
     // TODO: TBD: Call ozoneManager.getMultiTenantManager().deleteTenant() ?
 
-    return getOmRequest().toBuilder().setUserInfo(getUserInfo()).build();
+    return super.preExecute(ozoneManager);
   }
 
   @Override
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java
index 7d112135a7..991dbe85e3 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java
@@ -74,8 +74,10 @@ public class OMTenantRevokeAdminRequest extends OMClientRequest {
   @Override
   @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA)
   public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+
+    final OMRequest omRequest = super.preExecute(ozoneManager);
     final TenantRevokeAdminRequest request =
-        getOmRequest().getTenantRevokeAdminRequest();
+        omRequest.getTenantRevokeAdminRequest();
 
     final String accessId = request.getAccessId();
     String tenantId = request.getTenantId();
@@ -114,25 +116,16 @@ public class OMTenantRevokeAdminRequest extends OMClientRequest {
           OMException.ResultCodes.INVALID_TENANT_ID);
     }
 
-    // TODO: Call OMMTM to remove user from admin group of the tenant.
-    // The call should remove user (not accessId) from the tenant's admin group
-//    ozoneManager.getMultiTenantManager().revokeTenantAdmin();
+    // Remove user (inferred from access ID) from tenant admin role in Ranger
+    ozoneManager.getMultiTenantManager().revokeTenantAdmin(accessId);
 
-    final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
-        .setUserInfo(getUserInfo())
+    final OMRequest.Builder omRequestBuilder = omRequest.toBuilder()
         .setTenantRevokeAdminRequest(
-                // Regenerate request just in case tenantId is not provided
-                //  by the client
-                TenantRevokeAdminRequest.newBuilder()
-                        .setTenantId(tenantId)
-                        .setAccessId(request.getAccessId())
-                        .build())
-        .setCmdType(getOmRequest().getCmdType())
-        .setClientId(getOmRequest().getClientId());
-
-    if (getOmRequest().hasTraceID()) {
-      omRequestBuilder.setTraceID(getOmRequest().getTraceID());
-    }
+            // Regen request just in case tenantId is not provided by the client
+            TenantRevokeAdminRequest.newBuilder()
+                .setTenantId(tenantId)
+                .setAccessId(request.getAccessId())
+                .build());
 
     return omRequestBuilder.build();
   }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java
index e779ec8a1a..24ff96e103 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java
@@ -82,8 +82,10 @@ public class OMTenantRevokeUserAccessIdRequest extends OMClientRequest {
   @Override
   @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA)
   public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+
+    final OMRequest omRequest = super.preExecute(ozoneManager);
     final TenantRevokeUserAccessIdRequest request =
-        getOmRequest().getTenantRevokeUserAccessIdRequest();
+        omRequest.getTenantRevokeUserAccessIdRequest();
 
     final String accessId = request.getAccessId();
 
@@ -129,19 +131,12 @@ public class OMTenantRevokeUserAccessIdRequest extends OMClientRequest {
     // TODO: Check destroyUser() behavior
     ozoneManager.getMultiTenantManager().revokeUserAccessId(accessId);
 
-    final Builder omRequestBuilder = getOmRequest().toBuilder()
-        .setUserInfo(getUserInfo())
+    final Builder omRequestBuilder = omRequest.toBuilder()
         .setTenantRevokeUserAccessIdRequest(
             TenantRevokeUserAccessIdRequest.newBuilder()
                 .setAccessId(accessId)
                 .setTenantId(tenantId)
-                .build())
-        .setCmdType(getOmRequest().getCmdType())
-        .setClientId(getOmRequest().getClientId());
-
-    if (getOmRequest().hasTraceID()) {
-      omRequestBuilder.setTraceID(getOmRequest().getTraceID());
-    }
+                .build());
 
     return omRequestBuilder.build();
   }
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
index 5ab4a3b051..4dffc39e22 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
@@ -235,16 +235,19 @@ public class OzoneManagerRequestHandler implements RequestHandler {
         responseBuilder.setGetS3VolumeContextResponse(s3VolumeContextResponse);
         break;
       case TenantGetUserInfo:
+        impl.checkS3MultiTenancyEnabled();
         TenantGetUserInfoResponse getUserInfoResponse = tenantGetUserInfo(
             request.getTenantGetUserInfoRequest());
         responseBuilder.setTenantGetUserInfoResponse(getUserInfoResponse);
         break;
       case ListTenant:
+        impl.checkS3MultiTenancyEnabled();
         ListTenantResponse listTenantResponse = listTenant(
             request.getListTenantRequest());
         responseBuilder.setListTenantResponse(listTenantResponse);
         break;
       case TenantListUser:
+        impl.checkS3MultiTenancyEnabled();
         TenantListUserResponse listUserResponse = tenantListUsers(
             request.getTenantListUserRequest());
         responseBuilder.setTenantListUserResponse(listUserResponse);
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManager.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManager.java
new file mode 100644
index 0000000000..8fc0861838
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManager.java
@@ -0,0 +1,175 @@
+/*
+ * 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.om;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
+import org.apache.hadoop.ozone.om.request.OMRequestTestUtils;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
+import org.apache.hadoop.ozone.protocolPB.OzoneManagerRequestHandler;
+import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
+import org.apache.hadoop.util.StringUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+
+import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_MULTITENANCY_ENABLED;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RANGER_HTTPS_ADMIN_API_USER;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_RANGER_HTTPS_ADDRESS_KEY;
+import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FEATURE_NOT_ENABLED;
+
+/**
+ * Tests OMMultiTenantManager.
+ */
+public class TestOMMultiTenantManager {
+
+  /**
+   * Try different configs against
+   * OMMultiTenantManager#checkAndEnableMultiTenancy and verify its response.
+   */
+  @Test
+  public void testMultiTenancyCheckConfig() {
+    final OzoneManager ozoneManager = Mockito.mock(OzoneManager.class);
+
+    final OzoneConfiguration conf = new OzoneConfiguration();
+
+    // Case 1: ozone.om.multitenancy.enabled = false
+    conf.setBoolean(OZONE_OM_MULTITENANCY_ENABLED, false);
+    Assert.assertFalse(
+        OMMultiTenantManager.checkAndEnableMultiTenancy(ozoneManager, conf));
+
+    // Case 2: ozone.om.multitenancy.enabled = true
+    // Initially however none of the other essential configs are set.
+    conf.setBoolean(OZONE_OM_MULTITENANCY_ENABLED, true);
+    expectConfigCheckToFail(ozoneManager, conf);
+
+    // "Enable" security
+    Mockito.when(ozoneManager.isSecurityEnabled()).thenReturn(true);
+    expectConfigCheckToFail(ozoneManager, conf);
+
+    // Enable Kerberos auth
+    conf.set(HADOOP_SECURITY_AUTHENTICATION,
+        StringUtils.toLowerCase(AuthenticationMethod.KERBEROS.toString()));
+    expectConfigCheckToFail(ozoneManager, conf);
+
+    // Set essential Ranger conf one by one
+    conf.set(OZONE_RANGER_HTTPS_ADDRESS_KEY, "http://ranger:6080");
+    expectConfigCheckToFail(ozoneManager, conf);
+    conf.set(OZONE_OM_RANGER_HTTPS_ADMIN_API_USER, "admin");
+    expectConfigCheckToFail(ozoneManager, conf);
+    conf.set(OZONE_OM_RANGER_HTTPS_ADMIN_API_PASSWD, "passwd");
+    // At this point the config check should pass. Method returns true
+    Assert.assertTrue(
+        OMMultiTenantManager.checkAndEnableMultiTenancy(ozoneManager, conf));
+  }
+
+  /**
+   * Helper function for testMultiTenancyConfig.
+   */
+  private void expectConfigCheckToFail(OzoneManager ozoneManager,
+      OzoneConfiguration conf) {
+    try {
+      OMMultiTenantManager.checkAndEnableMultiTenancy(ozoneManager, conf);
+      Assert.fail("Should have thrown RuntimeException");
+    } catch (RuntimeException e) {
+      Assert.assertTrue(e.getMessage().contains("Failed to meet"));
+    }
+  }
+
+  /**
+   * Verify that Multi-Tenancy read and write requests are blocked as intended
+   * when the the feature is disabled.
+   */
+  @Test
+  public void testMultiTenancyRequestsWhenDisabled() throws IOException {
+
+    final OzoneManager ozoneManager = Mockito.mock(OzoneManager.class);
+    Mockito.doCallRealMethod().when(ozoneManager).checkS3MultiTenancyEnabled();
+
+    Mockito.when(ozoneManager.isS3MultiTenancyEnabled()).thenReturn(false);
+
+    final String tenantId = "test-tenant";
+    final String userPrincipal = "alice";
+    final String accessId =
+        OMMultiTenantManager.getDefaultAccessId(tenantId, userPrincipal);
+
+    // Check that Multi-Tenancy write requests are blocked when not enabled
+    expectWriteRequestToFail(ozoneManager,
+        OMRequestTestUtils.createTenantRequest(tenantId));
+    expectWriteRequestToFail(ozoneManager,
+        OMRequestTestUtils.deleteTenantRequest(tenantId));
+    expectWriteRequestToFail(ozoneManager,
+        OMRequestTestUtils.tenantAssignUserAccessIdRequest(
+            userPrincipal, tenantId, accessId));
+    expectWriteRequestToFail(ozoneManager,
+        OMRequestTestUtils.tenantRevokeUserAccessIdRequest(accessId));
+    expectWriteRequestToFail(ozoneManager,
+        OMRequestTestUtils.tenantAssignAdminRequest(accessId, tenantId, true));
+    expectWriteRequestToFail(ozoneManager,
+        OMRequestTestUtils.tenantRevokeAdminRequest(accessId, tenantId));
+
+    // Check that Multi-Tenancy read requests are blocked when not enabled
+    final OzoneManagerRequestHandler ozoneManagerRequestHandler =
+        new OzoneManagerRequestHandler(ozoneManager, null);
+
+    expectReadRequestToFail(ozoneManagerRequestHandler,
+        OMRequestTestUtils.listUsersInTenantRequest(tenantId));
+    expectReadRequestToFail(ozoneManagerRequestHandler,
+        OMRequestTestUtils.listTenantRequest());
+    expectReadRequestToFail(ozoneManagerRequestHandler,
+        OMRequestTestUtils.tenantGetUserInfoRequest(tenantId));
+
+    // getS3VolumeContext request does not throw exception when MT is disabled.
+    // Rather, it falls back to the default s3v for backwards compatibility.
+  }
+
+  /**
+   * Helper function for testMultiTenancyRPCWhenDisabled.
+   */
+  private void expectWriteRequestToFail(OzoneManager om, OMRequest omRequest)
+      throws IOException {
+    try {
+      OzoneManagerRatisUtils.createClientRequest(omRequest, om);
+      Assert.fail("Should have thrown OMException");
+    } catch (OMException e) {
+      Assert.assertEquals(FEATURE_NOT_ENABLED, e.getResult());
+    }
+  }
+
+  /**
+   * Helper function for testMultiTenancyRPCWhenDisabled.
+   */
+  private void expectReadRequestToFail(OzoneManagerRequestHandler handler,
+      OMRequest omRequest) {
+
+    // handleReadRequest does not throw
+    OMResponse omResponse = handler.handleReadRequest(omRequest);
+    Assert.assertFalse(omResponse.getSuccess());
+    Assert.assertEquals(Status.FEATURE_NOT_ENABLED, omResponse.getStatus());
+    Assert.assertTrue(omResponse.getMessage()
+        .startsWith("S3 multi-tenancy feature is not enabled"));
+  }
+
+}
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
index 75b486e8e8..1a6b76e1a9 100644
--- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
@@ -47,6 +47,10 @@ import org.apache.hadoop.ozone.om.helpers.OzoneFSUtils;
 import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteTenantRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3VolumeContextRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ListTenantRequest;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
     .MultipartUploadAbortRequest;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
@@ -67,6 +71,13 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
     .RemoveAclRequest;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos
     .SetAclRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignAdminRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignUserAccessIdRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantGetUserInfoRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantListUserRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeAdminRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeUserAccessIdRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
 import org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType;
 import org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType;
@@ -899,6 +910,149 @@ public final class OMRequestTestUtils {
         .setClientId(UUID.randomUUID().toString()).build();
   }
 
+  public static OMRequest createTenantRequest(String tenantId) {
+
+    final CreateTenantRequest.Builder requestBuilder =
+        CreateTenantRequest.newBuilder()
+            .setTenantId(tenantId)
+            .setVolumeName(tenantId);
+
+    return OMRequest.newBuilder()
+        .setCreateTenantRequest(requestBuilder)
+        .setCmdType(Type.CreateTenant)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest deleteTenantRequest(String tenantId) {
+
+    final DeleteTenantRequest.Builder requestBuilder =
+        DeleteTenantRequest.newBuilder()
+            .setTenantId(tenantId);
+
+    return OMRequest.newBuilder()
+        .setDeleteTenantRequest(requestBuilder)
+        .setCmdType(Type.DeleteTenant)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest tenantAssignUserAccessIdRequest(
+      String username, String tenantId, String accessId) {
+
+    final TenantAssignUserAccessIdRequest.Builder requestBuilder =
+        TenantAssignUserAccessIdRequest.newBuilder()
+            .setUserPrincipal(username)
+            .setTenantId(tenantId)
+            .setAccessId(accessId);
+
+    return OMRequest.newBuilder()
+        .setTenantAssignUserAccessIdRequest(requestBuilder)
+        .setCmdType(Type.TenantAssignUserAccessId)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest tenantRevokeUserAccessIdRequest(String accessId) {
+
+    final TenantRevokeUserAccessIdRequest.Builder requestBuilder =
+        TenantRevokeUserAccessIdRequest.newBuilder()
+            .setAccessId(accessId);
+
+    return OMRequest.newBuilder()
+        .setTenantRevokeUserAccessIdRequest(requestBuilder)
+        .setCmdType(Type.TenantRevokeUserAccessId)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest tenantAssignAdminRequest(
+      String accessId, String tenantId, boolean delegated) {
+
+    final TenantAssignAdminRequest.Builder requestBuilder =
+        TenantAssignAdminRequest.newBuilder()
+            .setAccessId(accessId)
+            .setDelegated(delegated);
+
+    if (tenantId != null) {
+      requestBuilder.setTenantId(tenantId);
+    }
+
+    return OMRequest.newBuilder()
+        .setTenantAssignAdminRequest(requestBuilder)
+        .setCmdType(Type.TenantAssignAdmin)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest tenantRevokeAdminRequest(
+      String accessId, String tenantId) {
+
+    final TenantRevokeAdminRequest.Builder requestBuilder =
+        TenantRevokeAdminRequest.newBuilder()
+            .setAccessId(accessId);
+
+    if (tenantId != null) {
+      requestBuilder.setTenantId(tenantId);
+    }
+
+    return OMRequest.newBuilder()
+        .setTenantRevokeAdminRequest(requestBuilder)
+        .setCmdType(Type.TenantRevokeAdmin)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest tenantGetUserInfoRequest(String userPrincipal) {
+
+    final TenantGetUserInfoRequest.Builder requestBuilder =
+        TenantGetUserInfoRequest.newBuilder()
+            .setUserPrincipal(userPrincipal);
+
+    return OMRequest.newBuilder()
+        .setTenantGetUserInfoRequest(requestBuilder)
+        .setCmdType(Type.TenantGetUserInfo)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest listUsersInTenantRequest(String tenantId) {
+
+    final TenantListUserRequest.Builder requestBuilder =
+        TenantListUserRequest.newBuilder()
+            .setTenantId(tenantId);
+
+    return OMRequest.newBuilder()
+        .setTenantListUserRequest(requestBuilder)
+        .setCmdType(Type.TenantListUser)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest listTenantRequest() {
+
+    final ListTenantRequest.Builder requestBuilder =
+        ListTenantRequest.newBuilder();
+
+    return OMRequest.newBuilder()
+        .setListTenantRequest(requestBuilder)
+        .setCmdType(Type.ListTenant)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
+  public static OMRequest getS3VolumeContextRequest() {
+
+    final GetS3VolumeContextRequest.Builder requestBuilder =
+        GetS3VolumeContextRequest.newBuilder();
+
+    return OMRequest.newBuilder()
+        .setGetS3VolumeContextRequest(requestBuilder)
+        .setCmdType(Type.GetS3VolumeContext)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+  }
+
   /**
    * Add the Key information to OzoneManager DB and cache.
    * @param omMetadataManager
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
index 4864de95b1..c7bdd8f994 100644
--- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
@@ -366,8 +366,6 @@ public class TestS3GetSecretRequest {
 
     // Additional mock setup needed to pass accessId check
     when(ozoneManager.getMultiTenantManager()).thenReturn(omMultiTenantManager);
-    when(omMultiTenantManager.getDefaultAccessId(TENANT_ID, USER_BOB))
-        .thenReturn(ACCESS_ID_BOB);
 
     // Run preExecute
     OMTenantAssignUserAccessIdRequest omTenantAssignUserAccessIdRequest =


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org