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

avijayan 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 dae8c0d  HDDS-5834 [Multi-Tenant] Implement ListUsersInTenant, Remove 
in memory maps. (#2759)
dae8c0d is described below

commit dae8c0df17f90c9c395fb306e5db5bed3ce44f26
Author: avijayanhwx <[email protected]>
AuthorDate: Thu Nov 4 10:41:57 2021 -0700

    HDDS-5834 [Multi-Tenant] Implement ListUsersInTenant, Remove in memory 
maps. (#2759)
---
 .../java/org/apache/hadoop/ozone/OzoneConsts.java  |   1 +
 .../apache/hadoop/ozone/client/ObjectStore.java    |   6 +
 .../ozone/client/protocol/ClientProtocol.java      |  11 +
 .../apache/hadoop/ozone/client/rpc/RpcClient.java  |   7 +
 .../main/java/org/apache/hadoop/ozone/OmUtils.java |   1 +
 .../org/apache/hadoop/ozone/audit/OMAction.java    |   3 +-
 .../hadoop/ozone/om/exceptions/OMException.java    |   3 +-
 .../hadoop/ozone/om/helpers/TenantUserList.java    |  80 ++++++
 ...leTenantImpl.java => DefaultOzoneS3Tenant.java} |  20 +-
 .../om/multitenant/OzoneTenantRolePrincipal.java   |   2 +-
 .../impl}/AccountNameSpaceImpl.java                |   2 +-
 .../impl/SingleVolumeTenantNamespace.java}         |  34 ++-
 .../impl}/package-info.java                        |   2 +-
 .../ozone/om/protocol/OzoneManagerProtocol.java    |   4 +
 ...OzoneManagerProtocolClientSideTranslatorPB.java |  19 ++
 ...estMultiTenantAccessAuthorizerRangerPlugin.java |   1 +
 .../hadoop/ozone/shell/TestOzoneTenantShell.java   |  35 ++-
 .../src/main/proto/OmClientProtocol.proto          |  23 ++
 .../hadoop/ozone/om/OMMultiTenantManager.java      |  45 ++--
 .../hadoop/ozone/om/OMMultiTenantManagerImpl.java  | 291 +++++++++++----------
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |  65 ++++-
 .../ozone/om/multitenant/CachedTenantInfo.java     |  45 ++++
 .../hadoop/ozone/om/multitenant}/package-info.java |  12 +-
 .../s3/tenant/OMAssignUserToTenantRequest.java     |   4 +-
 .../request/s3/tenant/OMTenantCreateRequest.java   |  18 +-
 .../protocolPB/OzoneManagerRequestHandler.java     |  24 ++
 .../ozone/om/TestOMMultiTenantManagerImpl.java     | 138 ++++++++++
 .../om/request/key/TestOMKeyAclRequestWithFSO.java |   2 +-
 .../s3/security/TestS3GetSecretRequest.java        |   4 +-
 .../ozone/shell/tenant/TenantListUsersHandler.java |  71 +++++
 .../ozone/shell/tenant/TenantUserCommands.java     |   3 +-
 31 files changed, 748 insertions(+), 228 deletions(-)

diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
index 9eb96db..36f5e52 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
@@ -334,6 +334,7 @@ public final class OzoneConsts {
   public static final String SOURCE_VOLUME = "sourceVolume";
   public static final String SOURCE_BUCKET = "sourceBucket";
   public static final String TENANT = "tenant";
+  public static final String USER_PREFIX = "userPrefix";
 
   // For multi-tenancy
   public static final String TENANT_NAME_USER_NAME_DELIMITER = "$";
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
index ba6f353..41a471f 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
@@ -36,6 +36,7 @@ import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
 import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
 import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
 import org.apache.hadoop.security.UserGroupInformation;
@@ -242,6 +243,11 @@ public class ObjectStore {
     proxy.tenantRevokeAdmin(accessId, tenantName);
   }
 
+  public TenantUserList listUsersInTenant(String tenantName, String userPrefix)
+      throws IOException {
+    return proxy.listUsersInTenant(tenantName, userPrefix);
+  }
+
   /**
    * Get tenant info for a user.
    * @param userPrincipal Kerberos principal of a user.
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index 3845250..9eec9d0 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -49,6 +49,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
 import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
 import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRoleInfo;
 import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
@@ -627,6 +628,16 @@ public interface ClientProtocol {
       throws IOException;
 
   /**
+   * Get List of users in a tenant.
+   * @param tenantName tenant name
+   * @param prefix optional prefix
+   * @return List of username, accessIds in tenant.
+   * @throws IOException on server error.
+   */
+  TenantUserList listUsersInTenant(String tenantName, String prefix)
+      throws IOException;
+
+  /**
    * List tenants.
    * @return TenantInfoList
    * @throws IOException
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index eabbfbb..aebfff0 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -106,6 +106,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
 import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
 import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
 import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
 import org.apache.hadoop.ozone.om.protocolPB.OmTransport;
 import org.apache.hadoop.ozone.om.protocolPB.OmTransportFactory;
@@ -734,6 +735,12 @@ public class RpcClient implements ClientProtocol {
   }
 
   @Override
+  public TenantUserList listUsersInTenant(String tenantName, String prefix)
+      throws IOException {
+    return ozoneManagerClient.listUsersInTenant(tenantName, prefix);
+  }
+
+  @Override
   public void setBucketVersioning(
       String volumeName, String bucketName, Boolean versioning)
       throws IOException {
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index 8e80634..a24e770 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -268,6 +268,7 @@ public final class OmUtils {
     case GetS3Volume:
     case ListTenant:
     case TenantGetUserInfo:
+    case TenantListUser:
       return true;
     case CreateVolume:
     case SetVolumeProperty:
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
index 5d37413..c95f74d 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
@@ -83,7 +83,8 @@ public enum OMAction implements AuditAction {
   TENANT_REVOKE_USER_ACCESSID,
 
   TENANT_ASSIGN_ADMIN,
-  TENANT_REVOKE_ADMIN;
+  TENANT_REVOKE_ADMIN,
+  TENANT_LIST_USER;
 
   @Override
   public String getAction() {
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 7b8502e..a049a20 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
@@ -251,6 +251,7 @@ public class OMException extends IOException {
     TENANT_USER_ACCESSID_NOT_FOUND,
     TENANT_USER_ACCESSID_ALREADY_EXISTS,
     INVALID_TENANT_USER_NAME,
-    INVALID_ACCESSID
+    INVALID_ACCESSID,
+    TENANT_AUTHORIZER_ERROR
   }
 }
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserList.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserList.java
new file mode 100644
index 0000000..acdaa7f
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserList.java
@@ -0,0 +1,80 @@
+/*
+ * 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.helpers;
+
+import java.util.List;
+import java.util.Objects;
+
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantListUserResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
+
+/**
+ * Class to encapsulate the list of users and corresponding accessIds
+ * associated with a tenant.
+ */
+public class TenantUserList {
+
+  private final String tenantName;
+
+  private final List<TenantUserAccessId> userAccessIds;
+
+
+  public TenantUserList(String tenantName,
+                        List<TenantUserAccessId> userAccessIds) {
+    this.tenantName = tenantName;
+    this.userAccessIds = userAccessIds;
+  }
+
+  public String getTenantName() {
+    return tenantName;
+  }
+
+  public List<TenantUserAccessId> getUserAccessIds() {
+    return userAccessIds;
+  }
+
+  public static TenantUserList fromProtobuf(TenantListUserResponse response) {
+    return new TenantUserList(response.getTenantName(),
+        response.getUserAccessIdInfoList());
+  }
+
+  @Override
+  public String toString() {
+    return "tenantName=" + tenantName +
+        "\nuserAccessIds=" + userAccessIds;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    TenantUserList that = (TenantUserList) o;
+    return tenantName.equals(that.tenantName) &&
+        userAccessIds.equals(that.userAccessIds);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(tenantName, userAccessIds);
+  }
+}
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/CephCompatibleTenantImpl.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/DefaultOzoneS3Tenant.java
similarity index 73%
rename from 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/CephCompatibleTenantImpl.java
rename to 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/DefaultOzoneS3Tenant.java
index 03d2b6a..48685f2 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/CephCompatibleTenantImpl.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/DefaultOzoneS3Tenant.java
@@ -17,38 +17,28 @@
  */
 package org.apache.hadoop.ozone.om.multitenant;
 
-import static 
org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
-import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.hadoop.ozone.om.multitenantImpl.AccountNameSpaceImpl;
-import org.apache.hadoop.ozone.om.multitenantImpl.BucketNameSpaceImpl;
-import org.apache.hadoop.ozone.security.acl.OzoneObj;
-import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.hadoop.ozone.om.multitenant.impl.AccountNameSpaceImpl;
+import org.apache.hadoop.ozone.om.multitenant.impl.SingleVolumeTenantNamespace;
 
 /**
  * Implements Tenant.
  */
-public class CephCompatibleTenantImpl implements Tenant {
+public class DefaultOzoneS3Tenant implements Tenant {
   private final String tenantID;
   private List<String> tenantRoleIds;
   private List<AccessPolicy> accessPolicies;
   private final AccountNameSpace accountNameSpace;
   private final BucketNameSpace bucketNameSpace;
 
-  public CephCompatibleTenantImpl(String id) {
+  public DefaultOzoneS3Tenant(String id) {
     tenantID = id;
     accessPolicies = new ArrayList<>();
     tenantRoleIds = new ArrayList<>();
     accountNameSpace = new AccountNameSpaceImpl(id);
-    bucketNameSpace = new BucketNameSpaceImpl(id);
-    OzoneObj volume = new OzoneObjInfo.Builder()
-        .setResType(VOLUME)
-        .setStoreType(OZONE)
-        .setVolumeName(bucketNameSpace.getBucketNameSpaceID()).build();
-    bucketNameSpace.addBucketNameSpaceObject(volume);
+    bucketNameSpace = new SingleVolumeTenantNamespace(id);
   }
 
   @Override
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
index 3aa3e3d..7f2e651 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/OzoneTenantRolePrincipal.java
@@ -53,4 +53,4 @@ public final class OzoneTenantRolePrincipal implements 
Principal {
   public String getName() {
     return tenantID + OzoneConsts.TENANT_NAME_ROLE_DELIMITER + roleName;
   }
-}
+}
\ No newline at end of file
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/AccountNameSpaceImpl.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/AccountNameSpaceImpl.java
similarity index 96%
rename from 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/AccountNameSpaceImpl.java
rename to 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/AccountNameSpaceImpl.java
index 93d84c2..e689b22 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/AccountNameSpaceImpl.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/AccountNameSpaceImpl.java
@@ -15,7 +15,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package org.apache.hadoop.ozone.om.multitenantImpl;
+package org.apache.hadoop.ozone.om.multitenant.impl;
 
 import org.apache.hadoop.hdds.client.OzoneQuota;
 import org.apache.hadoop.hdds.fs.SpaceUsageSource;
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/BucketNameSpaceImpl.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/SingleVolumeTenantNamespace.java
similarity index 59%
rename from 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/BucketNameSpaceImpl.java
rename to 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/SingleVolumeTenantNamespace.java
index 88f7f36..df343c4 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/BucketNameSpaceImpl.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/SingleVolumeTenantNamespace.java
@@ -15,26 +15,39 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package org.apache.hadoop.ozone.om.multitenantImpl;
+package org.apache.hadoop.ozone.om.multitenant.impl;
+
+import static 
org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
+import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.hadoop.hdds.client.OzoneQuota;
 import org.apache.hadoop.hdds.fs.SpaceUsageSource;
 import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+
+import com.google.common.collect.ImmutableList;
 
 /**
- * Implements BucketNameSpace.
+ * Implements BucketNameSpace which allows exactly ONE VOLUME.
  */
-public class BucketNameSpaceImpl implements BucketNameSpace {
+public class SingleVolumeTenantNamespace implements BucketNameSpace {
+  public static final String BUCKET_NS_PREFIX = "BucketNS-";
   private final String bucketNameSpaceID;
-  private List<OzoneObj> bucketNameSpaceObjects;
+  private final List<OzoneObj> nsObjects;
+
+  public SingleVolumeTenantNamespace(String id) {
+    this(id, id);
+  }
 
-  public BucketNameSpaceImpl(String id) {
-    bucketNameSpaceID = id;
-    bucketNameSpaceObjects = new ArrayList<>();
+  public SingleVolumeTenantNamespace(String id, String volumeName) {
+    this.bucketNameSpaceID = BUCKET_NS_PREFIX + id;
+    this.nsObjects = ImmutableList.of(new OzoneObjInfo.Builder()
+        .setResType(VOLUME)
+        .setStoreType(OZONE)
+        .setVolumeName(volumeName).build());
   }
 
   @Override
@@ -44,12 +57,13 @@ public class BucketNameSpaceImpl implements BucketNameSpace 
{
 
   @Override
   public List<OzoneObj> getBucketNameSpaceObjects() {
-    return bucketNameSpaceObjects;
+    return nsObjects;
   }
 
   @Override
   public void addBucketNameSpaceObject(OzoneObj e) {
-    bucketNameSpaceObjects.add(e);
+    throw new UnsupportedOperationException("Cannot add an object to a single" 
+
+        " SingleVolumeTenantNamespace.");
   }
 
   @Override
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/package-info.java
similarity index 94%
copy from 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
copy to 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/package-info.java
index 0b46716..60429c3 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/impl/package-info.java
@@ -20,4 +20,4 @@
 /**
  * Package contains classes related to Ozone multi-tenant implementation.
  */
-package org.apache.hadoop.ozone.om.multitenantImpl;
+package org.apache.hadoop.ozone.om.multitenant.impl;
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
index 9063dd5..06b328d 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
@@ -49,6 +49,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
 import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
 import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
 import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse;
@@ -513,6 +514,9 @@ public interface OzoneManagerProtocol
   TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
       throws IOException;
 
+  TenantUserList listUsersInTenant(String tenantName, String prefix)
+      throws IOException;
+
   /**
    * List tenants.
    * @return TenantInfoList
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 b01fe3a..168635e 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
@@ -58,6 +58,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
 import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
 import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
 import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AddAclRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AddAclResponse;
@@ -1014,6 +1015,24 @@ public final class 
OzoneManagerProtocolClientSideTranslatorPB
   }
 
   @Override
+  public TenantUserList listUsersInTenant(String tenantName, String prefix)
+      throws IOException {
+    TenantListUserRequest.Builder builder =
+        TenantListUserRequest.newBuilder().setTenantName(tenantName);
+    if (prefix != null) {
+      builder.setPrefix(prefix);
+    }
+    TenantListUserRequest request = builder.build();
+
+    final OMRequest omRequest = createOMRequest(Type.TenantListUser)
+        .setTenantListUserRequest(request).build();
+    final OMResponse response = submitRequest(omRequest);
+    final TenantListUserResponse resp =
+        handleError(response).getTenantListUserResponse();
+    return TenantUserList.fromProtobuf(resp);
+  }
+
+  @Override
   public OmVolumeArgs getS3Volume(String accessID) throws IOException {
     final GetS3VolumeRequest request = GetS3VolumeRequest.newBuilder()
         .setAccessID(accessID)
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
index 7532905..0eb52f9 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantAccessAuthorizerRangerPlugin.java
@@ -230,6 +230,7 @@ public class TestMultiTenantAccessAuthorizerRangerPlugin {
     return tenantVolumeAccessPolicy;
   }
 
+
   // TODO: REMOVE THIS?
   private AccessPolicy allowAccessBucketPolicy(String vol, String bucketName,
       String tenant) throws IOException {
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 8204273..4556d4f 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
@@ -228,7 +228,7 @@ public class TestOzoneTenantShell {
    * was thrown.
    */
   private void executeWithError(OzoneShell shell, String[] args,
-      String expectedError) {
+                                String expectedError) {
     if (Strings.isNullOrEmpty(expectedError)) {
       execute(shell, args);
     } else {
@@ -315,7 +315,7 @@ public class TestOzoneTenantShell {
    * Helper function that checks command output AND clears it.
    */
   private void checkOutput(ByteArrayOutputStream stream, String stringToMatch,
-      boolean exactMatch) throws IOException {
+                           boolean exactMatch) throws IOException {
     stream.flush();
     final String str = stream.toString(DEFAULT_ENCODING);
     checkOutput(str, stringToMatch, exactMatch);
@@ -323,7 +323,7 @@ public class TestOzoneTenantShell {
   }
 
   private void checkOutput(String str, String stringToMatch,
-      boolean exactMatch) {
+                           boolean exactMatch) {
     if (exactMatch) {
       Assert.assertEquals(stringToMatch, str);
     } else {
@@ -523,4 +523,31 @@ public class TestOzoneTenantShell {
     checkOutput(err, "Revoked accessId", false);
   }
 
-}
+  private void testListTenantUsers() throws IOException {
+    executeHA(tenantShell, new String[] {
+        "user", "assign", "[email protected]", "--tenant=research"});
+    checkOutput(out, "export AWS_ACCESS_KEY_ID='[email protected]'\n"
+        + "export AWS_SECRET_ACCESS_KEY='", false);
+    checkOutput(err, "Assigned '[email protected]' to 'research'" +
+        " with accessId '[email protected]'.\n", true);
+
+    executeHA(tenantShell, new String[] {
+        "user", "list", "--tenant=research"});
+    checkOutput(out,
+        "- User '[email protected]' with accessId '[email protected]'\n"
+            + "- User '[email protected]' with accessId 
'research$alice@EXAMPLE"
+            + ".COM'\n", true);
+    checkOutput(err, "", true);
+
+    executeHA(tenantShell, new String[] {
+        "user", "list", "--tenant=research", "--prefix=b"});
+    checkOutput(out, "- User '[email protected]' with accessId " +
+        "'[email protected]'\n", true);
+    checkOutput(err, "", true);
+
+    executeHA(tenantShell, new String[] {
+        "user", "list", "--tenant=unknown"});
+    checkOutput(err, "Failed to Get Users in tenant 'unknown': " +
+        "Tenant 'unknown' not found!\n", true);
+  }
+}
\ No newline at end of file
diff --git 
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto 
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 6867247..2cb1c66 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -116,6 +116,7 @@ enum Type {
   TenantRevokeAdmin = 102;
 
   GetS3Volume = 104;
+  TenantListUser = 105;
 }
 
 message OMRequest {
@@ -212,6 +213,7 @@ message OMRequest {
   optional TenantRevokeAdminRequest         TenantRevokeAdminRequest       = 
102;
 
   optional GetS3VolumeRequest               getS3VolumeRequest             = 
104;
+  optional TenantListUserRequest            tenantListUserRequest         = 
105;
 }
 
 message OMResponse {
@@ -303,6 +305,7 @@ message OMResponse {
   optional TenantRevokeAdminResponse         TenantRevokeAdminResponse     = 
102;
 
   optional GetS3VolumeResponse               getS3VolumeResponse           = 
104;
+  optional TenantListUserResponse            tenantListUserResponse        = 
105;
 }
 
 enum Status {
@@ -406,6 +409,7 @@ enum Status {
     TENANT_USER_ACCESSID_ALREADY_EXISTS = 79;  // TODO: Remove if not used
     INVALID_TENANT_USER_NAME = 80;
     INVALID_ACCESSID = 81;
+    TENANT_AUTHORIZER_ERROR = 82;
 }
 
 /**
@@ -1386,6 +1390,14 @@ message TenantInfo {
     optional string bucketPolicyGroupName = 5;
 }
 
+message TenantUserAccessId {
+    optional string user = 1;
+    optional string accessId = 2;
+    optional bool isAdmin = 3;
+    optional bool isDelegatedAdmin = 4;
+}
+
+
 message ListTenantRequest {
 
 }
@@ -1399,11 +1411,22 @@ message TenantGetUserInfoRequest {
     optional string userPrincipal = 1;
 }
 
+message TenantListUserRequest {
+    optional string tenantName = 1;
+    optional string prefix = 2;
+}
+
 message TenantGetUserInfoResponse {
     optional bool success = 1;
     optional TenantUserInfo tenantUserInfo = 2;
 }
 
+message TenantListUserResponse {
+    optional bool success = 1;
+    optional string tenantName = 2;
+    repeated TenantUserAccessId userAccessIdInfo = 3;
+}
+
 message TenantUserInfo {
     optional string userPrincipal = 1;
     repeated TenantAccessIdInfo accessIdInfo = 2;
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 5363e3b..c4d1793 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
@@ -20,7 +20,7 @@ import java.io.IOException;
 import java.util.List;
 
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
 import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
 import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
@@ -31,8 +31,8 @@ import org.apache.http.auth.BasicUserPrincipal;
  * OM MultiTenant manager interface.
  */
 public interface OMMultiTenantManager {
-  /**
-   * Start multi-tenant manager. Performs initialization e.g.
+  /*
+   * Init multi-tenant manager. Performs initialization e.g.
    *  - Initialize Multi-Tenant-Gatekeeper-Plugin
    *  - Validate Multi-Tenant Bucket-NameSpaces
    *  - Validate Multi-Tenant Account-NameSpaces
@@ -44,15 +44,14 @@ public interface OMMultiTenantManager {
    *       . OM-DB state <-in-sync-> IMultiTenantGateKeeperPluginState
    *       . OM DB state is always the source of truth.
    *
-   * @param configuration
    * @throws IOException
    */
-  void start(OzoneConfiguration configuration) throws IOException;
-
-  /**
-   * Stop multi-tenant manager.
-   */
-  void stop() throws Exception;
+//  void start() throws IOException;
+//
+//  /**
+//   * Stop multi-tenant manager.
+//   */
+//  void stop() throws Exception;
 
   /**
    * Returns the corresponding OzoneManager instance.
@@ -67,7 +66,8 @@ public interface OMMultiTenantManager {
    * @param tenantID
    * @return Tenant interface.
    */
-  Tenant createTenant(String tenantID) throws IOException;
+  Tenant createTenantAccessInAuthorizer(String tenantID) throws IOException;
+
 
   /**
    * Given a TenantID String, Return Tenant Interface. If the Tenant doesn't
@@ -106,7 +106,7 @@ public interface OMMultiTenantManager {
    * @return
    * @throws IOException
    */
-  void destroyTenant(Tenant tenant) throws Exception;
+  void removeTenantAccessFromAuthorizer(Tenant tenant) throws Exception;
 
 
   /**
@@ -121,15 +121,12 @@ public interface OMMultiTenantManager {
                             String accessID) throws IOException;
 
   /**
-   * Given a user, destroys all state associated with that user.
-   * This is different from deactivateUser().
+   * Revoke user accessId.
    * @param accessID
-   * @return
    * @throws IOException
    */
   void revokeUserAccessId(String accessID) throws IOException;
 
-
   /**
    * Given an accessId, return kerberos user name for the tenant user.
    */
@@ -160,6 +157,22 @@ public interface OMMultiTenantManager {
   void deactivateUser(String accessID) throws IOException;
 
   /**
+   * Check if a user is a tenant Admin.
+   * @param user user name.
+   * @param tenantName tenant name.
+   * @return
+   */
+  boolean isTenantAdmin(String user, String tenantName);
+
+  /**
+   * List all the user & accessIDs of all users that belong to this Tenant.
+   * @param tenantID
+   * @return List of users
+   */
+  TenantUserList listUsersInTenant(String tenantID, String prefix)
+      throws IOException;
+
+  /**
    * List all the access IDs of all users that belong to this Tenant.
    * @param tenantID
    * @return List of users
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 b82052e..1ffd6b4 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,6 +17,8 @@
  */
 package org.apache.hadoop.ozone.om;
 
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_ACCESSID;
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TENANT_AUTHORIZER_ERROR;
 import static 
org.apache.hadoop.ozone.om.multitenant.AccessPolicy.AccessGrantType.ALLOW;
 import static 
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL;
 import static 
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.CREATE;
@@ -30,22 +32,28 @@ import static 
org.apache.hadoop.ozone.security.acl.OzoneObj.ResourceType.VOLUME;
 import static org.apache.hadoop.ozone.security.acl.OzoneObj.StoreType.OZONE;
 
 import java.io.IOException;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
-import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.utils.db.Table;
+import org.apache.hadoop.hdds.utils.db.Table.KeyValue;
+import org.apache.hadoop.hdds.utils.db.TableIterator;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
 import org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
 import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
-import org.apache.hadoop.ozone.om.multitenant.CephCompatibleTenantImpl;
+import org.apache.hadoop.ozone.om.multitenant.CachedTenantInfo;
+import org.apache.hadoop.ozone.om.multitenant.DefaultOzoneS3Tenant;
 import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizer;
 import 
org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerDummyPlugin;
 import 
org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerRangerPlugin;
@@ -53,6 +61,7 @@ import 
org.apache.hadoop.ozone.om.multitenant.OzoneOwnerPrincipal;
 import org.apache.hadoop.ozone.om.multitenant.OzoneTenantRolePrincipal;
 import org.apache.hadoop.ozone.om.multitenant.RangerAccessPolicy;
 import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
 import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
 import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
@@ -60,6 +69,7 @@ import org.apache.http.auth.BasicUserPrincipal;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 /**
@@ -74,103 +84,47 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
   // Internal dev flag to skip Ranger communication.
   public static final String OZONE_OM_TENANT_DEV_SKIP_RANGER =
       "ozone.om.tenant.dev.skip.ranger";
-  private final boolean devSkipRanger;
 
   private MultiTenantAccessAuthorizer authorizer;
   private final OMMetadataManager omMetadataManager;
   private final OzoneConfiguration conf;
   private final ReentrantReadWriteLock controlPathLock;
-
-  // The following Mappings maintain all of the multi-tenancy states.
-  // These mappings needs to have their persistent counterpart in OM tables.
-  // Long term, we can bring those tables here as part of multi-tenant-Manager
-  // and not mixing up things with the rest of the OM. And thus giving this
-  // module a clean separation from the rest of the OM.
-
-  // key : tenantName, value : TenantInfo
-  private final Map<String, Tenant> inMemoryTenantNameToTenantInfoMap;
-
-  // This Mapping maintains all policies for all tenants
-  //   key = tenantName
-  //   value = list of all PolicyNames for this tenant in authorizor-plugin
-  // Typical Usage : find out all the bucket/user policies for a tenant.
-  private final Map<String, List<String>> inMemoryTenantToPolicyNameListMap;
-
-  // This Mapping maintains all groups for all tenants
-  //   key = tenantName
-  //   value = list of all GroupNames that belong to this tenant
-  // There are at least two default groups created for every tenant.
-  //    Tenant_XYZ$GroupTenantAllUsers
-  //    Tenant_XYZ$GroupTenantAdmins
-  // There are also predefined global groups like (TODO)
-  //    - AllAuthenticateUsers (TODO)
-  //    - AllUsers (TODO)
-  // Typical usage : Put together all the users that have access to some
-  // resource in the same group. E.g.
-  //      1) users in Tenant_XYZ$GroupTenantAllUsers would be able to
-  //      access the volume created for Tenant_XYZ.
-  //      2) If user creates an access policy for a bucket, all the users
-  //      that would have same access to the bucket can go in the same group.
-  private final Map<String, List<String>> inMemoryTenantToTenantGroups;
-
-  // Mapping for user-access-id to TenantName
-  // Typical usage: given a user-access-id find out which tenant
-  private final Map<String, String> inMemoryAccessIDToTenantNameMap;
-
-  // Mapping from user-access-id to all the groups that they belong to.
-  // Typical usage: Adding a user or modify user, provide a list of groups
-  //          that they would belong to. Note that groupIDs are opaque to OM.
-  //          This may make sense just to the authorizer-plugin.
-  private final Map<String, List<String>> inMemoryAccessIDToListOfGroupsMap;
-
-  // Used for testing (where there's no ranger instance) to inject a mock
-  // authorizer. Use the normal Ranger plugin by default.
-  private static Supplier<MultiTenantAccessAuthorizer> authorizerSupplier =
-      MultiTenantAccessAuthorizerRangerPlugin::new;
-
+  private final Map<String, CachedTenantInfo> tenantCache;
 
   OMMultiTenantManagerImpl(OMMetadataManager mgr, OzoneConfiguration conf)
       throws IOException {
     this.conf = conf;
-    inMemoryTenantNameToTenantInfoMap = new ConcurrentHashMap<>();
-    inMemoryTenantToPolicyNameListMap = new ConcurrentHashMap<>();
-    inMemoryTenantToTenantGroups = new ConcurrentHashMap<>();
-    inMemoryAccessIDToTenantNameMap = new ConcurrentHashMap<>();
-    inMemoryAccessIDToListOfGroupsMap = new ConcurrentHashMap<>();
-
     controlPathLock = new ReentrantReadWriteLock();
     omMetadataManager = mgr;
-
-    devSkipRanger = conf.getBoolean(OZONE_OM_TENANT_DEV_SKIP_RANGER, false);
-    start(conf);
-  }
-
-  @VisibleForTesting
-  public static void setAuthorizerSupplier(
-      Supplier<MultiTenantAccessAuthorizer> authSupplier) {
-    authorizerSupplier = authSupplier;
-  }
-
-  @Override
-  public void start(OzoneConfiguration configuration) throws IOException {
+    tenantCache = new ConcurrentHashMap<>();
+    boolean devSkipRanger = conf.getBoolean(OZONE_OM_TENANT_DEV_SKIP_RANGER,
+        false);
     if (devSkipRanger) {
       authorizer = new MultiTenantAccessAuthorizerDummyPlugin();
     } else {
       authorizer = new MultiTenantAccessAuthorizerRangerPlugin();
     }
-    authorizer.init(configuration);
+    authorizer.init(conf);
+    loadUsersFromDB();
   }
 
-  @Override
-  public void stop() throws Exception {
-
-  }
+// start() and stop() lifeycle methods can be added when there is a background
+// work going on.
+//  @Override
+//  public void start() throws IOException {
+//  }
+//
+//  @Override
+//  public void stop() throws Exception {
+//
+//  }
 
   @Override
   public OMMetadataManager getOmMetadataManager() {
     return omMetadataManager;
   }
 
+  // TODO: Cleanup up this Java doc.
   /**
    *  Algorithm
    *  OM State :
@@ -204,18 +158,12 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
    * @throws IOException
    */
   @Override
-  public Tenant createTenant(String tenantID) throws IOException {
+  public Tenant createTenantAccessInAuthorizer(String tenantID)
+      throws IOException {
 
-    Tenant tenant = new CephCompatibleTenantImpl(tenantID);
+    Tenant tenant = new DefaultOzoneS3Tenant(tenantID);
     try {
       controlPathLock.writeLock().lock();
-      inMemoryTenantNameToTenantInfoMap.put(tenantID, tenant);
-
-      // TODO : for now just create state in the Ranger. OM state is already
-      //  created in ValidateAndUpdateCache for the ratis transaction.
-
-      // TODO : Make it an idempotent operation. If any ranger state creation
-      //  fails because it already exists, Ignore it.
 
       // Create admin role first
       final OzoneTenantRolePrincipal adminRole =
@@ -229,10 +177,6 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
       String userRoleId = authorizer.createRole(userRole, adminRole.getName());
       tenant.addTenantAccessRole(userRoleId);
 
-      final List<String> allTenantRole =
-          Arrays.asList(userRole.getName(), adminRole.getName());
-      inMemoryTenantToTenantGroups.put(tenantID, allTenantRole);
-
       BucketNameSpace bucketNameSpace = tenant.getTenantBucketNameSpace();
       // bucket namespace is volume name ??
       for (OzoneObj volume : bucketNameSpace.getBucketNameSpaceObjects()) {
@@ -253,35 +197,24 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
         tenant.addTenantAccessPolicy(tenantBucketCreatePolicy);
       }
 
-      inMemoryTenantToPolicyNameListMap.put(tenantID,
-          tenant.getTenantAccessPolicies().stream().map(
-              AccessPolicy::getPolicyName).collect(Collectors.toList()));
+      tenantCache.put(tenantID, new CachedTenantInfo(tenantID));
     } catch (Exception e) {
       try {
-        destroyTenant(tenant);
+        removeTenantAccessFromAuthorizer(tenant);
       } catch (Exception exception) {
         // Best effort cleanup.
       }
-      controlPathLock.writeLock().unlock();
       throw new IOException(e.getMessage());
+    } finally {
+      controlPathLock.writeLock().unlock();
     }
-    controlPathLock.writeLock().unlock();
     return tenant;
   }
 
   @Override
   public Tenant getTenantInfo(String tenantID) throws IOException {
-    // TODO:Should read from DB. Ditch the in-memory maps.
-    if (!inMemoryTenantNameToTenantInfoMap.containsKey(tenantID)) {
-      return null;
-    }
-    for (Map.Entry<String, Tenant> entry :
-        inMemoryTenantNameToTenantInfoMap.entrySet()) {
-      if (entry.getKey().equals(tenantID)) {
-        return entry.getValue();
-      }
-    }
-    throw new IOException("All Tenants Map is corrupt");
+    // Todo : fix this.
+    return null;
   }
 
   @Override
@@ -290,9 +223,7 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
   }
 
   @Override
-  public void destroyTenant(Tenant tenant) throws Exception {
-    // TODO: Make sure this is idempotent. This can be called by ALL 3 OMs
-    //  in the case of a createTenant checkAcl failure for instance.
+  public void removeTenantAccessFromAuthorizer(Tenant tenant) throws Exception 
{
     try {
       controlPathLock.writeLock().lock();
       for (AccessPolicy policy : tenant.getTenantAccessPolicies()) {
@@ -301,29 +232,22 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
       for (String roleId : tenant.getTenantRoles()) {
         authorizer.deleteRole(roleId);
       }
-
-      inMemoryTenantNameToTenantInfoMap.remove(tenant.getTenantId());
-      inMemoryTenantToPolicyNameListMap.remove(tenant.getTenantId());
-      inMemoryTenantToTenantGroups.remove(tenant.getTenantId());
-    } catch (Exception e) {
+      if (tenantCache.containsKey(tenant.getTenantId())) {
+        LOG.info("Removing tenant {} from in memory cached state",
+            tenant.getTenantId());
+        tenantCache.remove(tenant.getTenantId());
+      }
+    }  finally {
       controlPathLock.writeLock().unlock();
-      throw e;
     }
-    controlPathLock.writeLock().unlock();
   }
 
   /**
    *  Algorithm
-   *  OM State :
-   *    - Validation (Part of Ratis Request)
-   *    - create user in OMDB {Part of RATIS request}
-   *    - Persistence to OM DB {Part of RATIS request}
    *  Authorizer-plugin(Ranger) State :
    *    - create User in Ranger DB
    *    - For every user created
    *        Add them to # GroupTenantAllUsers
-   *  Finally :
-   *    - Update all Maps maintained by Multi-Tenant-Manager
    *  In case of failure :
    *    - Undo all Ranger State
    *    - remove updates to the Map
@@ -340,27 +264,29 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
    */
   @Override
   public String assignUserToTenant(BasicUserPrincipal principal,
-      String tenantName, String accessID) throws IOException {
+                                 String tenantName,
+                                 String accessID) throws IOException {
+    ImmutablePair<String, String> userAccessIdPair =
+        new ImmutablePair<>(principal.getName(), accessID);
     try {
       controlPathLock.writeLock().lock();
-      Tenant tenant = getTenantInfo(tenantName);
-      if (tenant == null) {
-        LOG.error("Cannot assign user to tenant {} that doesn't exist",
-            tenantName);
-        return null;
-      }
+
+      LOG.info("Adding user '{}' to tenant '{}' in-memory state.",
+          principal.getName(), tenantName);
+      CachedTenantInfo cachedTenantInfo =
+          tenantCache.getOrDefault(tenantName,
+              new CachedTenantInfo(tenantName));
+      cachedTenantInfo.getTenantUsers().add(userAccessIdPair);
+
       final OzoneTenantRolePrincipal roleTenantAllUsers =
           OzoneTenantRolePrincipal.getUserRole(tenantName);
       String roleJsonStr = authorizer.getRole(roleTenantAllUsers);
       String roleId = authorizer.assignUser(principal, roleJsonStr, false);
-
-      inMemoryAccessIDToTenantNameMap.put(accessID, tenantName);
-//      inMemoryAccessIDToListOfGroupsMap.put(accessID, userRoleIds);
-
       return roleId;
-    } catch (IOException e) {
+    } catch (Exception e) {
       revokeUserAccessId(accessID);
-      throw e;
+      tenantCache.get(tenantName).getTenantUsers().remove(userAccessIdPair);
+      throw new OMException(e.getMessage(), TENANT_AUTHORIZER_ERROR);
     } finally {
       controlPathLock.writeLock().unlock();
     }
@@ -370,22 +296,29 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
   public void revokeUserAccessId(String accessID) throws IOException {
     try {
       controlPathLock.writeLock().lock();
-      String tenantName = getTenantForAccessID(accessID);
+      OmDBAccessIdInfo omDBAccessIdInfo =
+          omMetadataManager.getTenantAccessIdTable().get(accessID);
+      if (omDBAccessIdInfo == null) {
+        throw new OMException(INVALID_ACCESSID);
+      }
+      String tenantName = omDBAccessIdInfo.getTenantId();
       if (tenantName == null) {
         LOG.error("Tenant doesn't exist");
         return;
       }
+      tenantCache.get(tenantName).getTenantUsers()
+          .remove(new ImmutablePair<>(omDBAccessIdInfo.getKerberosPrincipal(),
+              accessID));
       // TODO: Determine how to replace this code.
 //      final String userID = authorizer.getUserId(userPrincipal);
 //      authorizer.deleteUser(userID);
 
-      inMemoryAccessIDToTenantNameMap.remove(accessID);
-      inMemoryAccessIDToListOfGroupsMap.remove(accessID);
     } finally {
       controlPathLock.writeLock().unlock();
     }
   }
 
+
   @Override
   public String getUserNameGivenAccessId(String accessId) {
     Preconditions.checkNotNull(accessId);
@@ -427,16 +360,53 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
   }
 
   @Override
-  public List<String> listAllAccessIDs(String tenantID)
+  public boolean isTenantAdmin(String user, String tenantName) {
+    return true;
+  }
+
+  @Override
+  public TenantUserList listUsersInTenant(String tenantID, String prefix)
       throws IOException {
-    return null;
+
+    if (!omMetadataManager.getTenantStateTable().isExist(tenantID)) {
+      throw new IOException("Tenant '" + tenantID + "' not found!");
+    }
+
+    List<TenantUserAccessId> userAccessIds = new ArrayList<>();
+    CachedTenantInfo cachedTenantInfo = tenantCache.get(tenantID);
+    if (cachedTenantInfo == null) {
+      throw new IOException("Inconsistent in memory Tenant cache '" + tenantID
+          + "' not found in cache, but present in OM DB!");
+    }
+
+    cachedTenantInfo.getTenantUsers().stream()
+        .filter(
+            k -> StringUtils.isEmpty(prefix) || k.getKey().startsWith(prefix))
+        .forEach(
+            k -> userAccessIds.add(
+                TenantUserAccessId.newBuilder()
+                    .setUser(k.getKey())
+                    .setAccessId(k.getValue())
+                    .build()));
+    return new TenantUserList(tenantID, userAccessIds);
   }
 
   @Override
-  public String getTenantForAccessID(String accessID) {
-    return inMemoryAccessIDToTenantNameMap.getOrDefault(accessID, null);
+  public String getTenantForAccessID(String accessID) throws IOException {
+    OmDBAccessIdInfo omDBAccessIdInfo =
+        omMetadataManager.getTenantAccessIdTable().get(accessID);
+    if (omDBAccessIdInfo == null) {
+      throw new OMException(INVALID_ACCESSID);
+    }
+    return omDBAccessIdInfo.getTenantId();
   }
 
+  public List<String> listAllAccessIDs(String tenantID)
+      throws IOException {
+    return null;
+  }
+
+
   @Override
   public void assignTenantAdmin(String accessID, boolean delegated)
       throws IOException {
@@ -615,4 +585,37 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
   public OzoneConfiguration getConf() {
     return conf;
   }
+
+  public void loadUsersFromDB() {
+    Table<String, OmDBAccessIdInfo> tenantAccessIdTable =
+        omMetadataManager.getTenantAccessIdTable();
+    TableIterator<String, ? extends KeyValue<String, OmDBAccessIdInfo>>
+        iterator = tenantAccessIdTable.iterator();
+    int userCount = 0;
+
+    try {
+      while (iterator.hasNext()) {
+        KeyValue<String, OmDBAccessIdInfo> next = iterator.next();
+        String accessId = next.getKey();
+        OmDBAccessIdInfo value = next.getValue();
+        String tenantId = value.getTenantId();
+        String user = value.getKerberosPrincipal();
+
+        CachedTenantInfo cachedTenantInfo = tenantCache
+            .computeIfAbsent(tenantId, k -> new CachedTenantInfo(tenantId));
+        cachedTenantInfo.getTenantUsers().add(
+            new ImmutablePair<>(user, accessId));
+        userCount++;
+      }
+      LOG.info("Loaded {} tenants and {} tenant-users from the database.",
+          tenantCache.size(), userCount);
+    } catch (Exception ex) {
+      LOG.error("Error while loading user list. ", ex);
+    }
+  }
+
+  @VisibleForTesting
+  Map<String, CachedTenantInfo> getTenantCache() {
+    return tenantCache;
+  }
 }
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 114bb20..4b59387 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
@@ -140,6 +140,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
 import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
 import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
 import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.protocol.OMInterServiceProtocol;
 import 
org.apache.hadoop.ozone.om.protocolPB.OMInterServiceProtocolClientSideImpl;
 import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
@@ -255,6 +256,7 @@ import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_VOLUME_LISTALL_AL
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.DETECTED_LOOP_IN_BUCKET_LINKS;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_AUTH_METHOD;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_REQUEST;
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_ACCESSID;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.KEY_NOT_FOUND;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER;
@@ -3213,27 +3215,64 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
   }
 
   @Override
-  public OmVolumeArgs getS3Volume(String accessID) throws IOException {
-    String tenantName = multiTenantManagr.getTenantForAccessID(accessID);
-    if (tenantName == null) {
-      // If the user is not associated with a tenant, they will use the
-      // default s3 volume.
-      String defaultS3volume =
-          HddsClientUtils.getDefaultS3VolumeName(configuration);
+  public TenantUserList listUsersInTenant(String tenantName, String prefix)
+      throws IOException {
 
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("No tenant found for access ID {}. Directing " +
-            "requests to default s3 volume {}.", accessID, defaultS3volume);
+    if (StringUtils.isEmpty(tenantName)) {
+      return null;
+    }
+
+    final Map<String, String> auditMap = new LinkedHashMap<>();
+    auditMap.put(OzoneConsts.TENANT, tenantName);
+    auditMap.put(OzoneConsts.USER_PREFIX, prefix);
+    try {
+      String userName = getRemoteUser().getUserName();
+      if (!multiTenantManagr.isTenantAdmin(userName, tenantName)
+          && !omAdminUsernames.contains(userName)) {
+        throw new IOException("Only tenant and ozone admins can access this " +
+            "API. '" + userName + "' is not an admin.");
       }
-      return getVolumeInfo(defaultS3volume);
-    } else {
+
+      final TenantUserList userList =
+          multiTenantManagr.listUsersInTenant(tenantName, prefix);
+      AUDIT.logReadSuccess(buildAuditMessageForSuccess(
+          OMAction.TENANT_LIST_USER, auditMap));
+      return userList;
+    } catch (IOException ex) {
+      AUDIT.logReadFailure(buildAuditMessageForFailure(
+          OMAction.TENANT_LIST_USER, auditMap, ex));
+      throw ex;
+    }
+  }
+
+  @Override
+  public OmVolumeArgs getS3Volume(String accessID) throws IOException {
+
+    String tenantName;
+    try {
+      tenantName = multiTenantManagr.getTenantForAccessID(accessID);
       if (LOG.isDebugEnabled()) {
         LOG.debug("Get S3 volume request for access ID {} belonging to tenant" 
+
-            " {} is directed to the volume {}.", accessID, tenantName,
+                " {} is directed to the volume {}.", accessID, tenantName,
             tenantName);
       }
       // This call performs acl checks and checks volume existence.
       return getVolumeInfo(tenantName);
+
+    } catch (OMException ex) {
+      if (ex.getResult().equals(INVALID_ACCESSID)) {
+        // If the user is not associated with a tenant, they will use the
+        // default s3 volume.
+        String defaultS3volume =
+            HddsClientUtils.getDefaultS3VolumeName(configuration);
+
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("No tenant found for access ID {}. Directing " +
+              "requests to default s3 volume {}.", accessID, defaultS3volume);
+        }
+        return getVolumeInfo(defaultS3volume);
+      }
+      throw ex;
     }
   }
 
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/CachedTenantInfo.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/CachedTenantInfo.java
new file mode 100644
index 0000000..b93eea2
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/CachedTenantInfo.java
@@ -0,0 +1,45 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.multitenant;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * A collection of things that we want to maintain about a tenant in memory.
+ */
+public class CachedTenantInfo {
+
+  private String tenantId;
+  private Set<Pair<String, String>> tenantUserAccessIds;
+
+  public CachedTenantInfo(String tenantId) {
+    this.tenantId = tenantId;
+    tenantUserAccessIds = new HashSet<>();
+  }
+
+  public Set<Pair<String, String>> getTenantUsers() {
+    return tenantUserAccessIds;
+  }
+
+  public String getTenantId() {
+    return tenantId;
+  }
+}
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/package-info.java
similarity index 73%
rename from 
hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
rename to 
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/package-info.java
index 0b46716..5be4f96 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenantImpl/package-info.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/multitenant/package-info.java
@@ -1,23 +1,23 @@
-/*
+/**
  * 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
+ * with the License.  You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     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.multitenant;
+
 /**
- * Package contains classes related to Ozone multi-tenant implementation.
+ * This package contains classes related to Ozone Multi tenancy.
  */
-package org.apache.hadoop.ozone.om.multitenantImpl;
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
index 9b4a747..a2af9ca 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
@@ -219,7 +219,8 @@ public class OMAssignUserToTenantRequest extends 
OMClientRequest {
     final TenantAssignUserAccessIdRequest request =
         getOmRequest().getTenantAssignUserAccessIdRequest();
     final String tenantName = request.getTenantName();
-    final String principal = request.getTenantUsername();  // TODO: Rename this
+    final String principal = request.getTenantUsername();
+
     assert(accessId.equals(request.getAccessId()));
     IOException exception = null;
 
@@ -341,7 +342,6 @@ public class OMAssignUserToTenantRequest extends 
OMClientRequest {
           omResponse.build(), s3SecretValue, principal, defaultGroupName,
           roleName, accessId, omDBAccessIdInfo, principalInfo);
     } catch (IOException ex) {
-      // Error handling
       handleRequestFailure(ozoneManager);
       exception = ex;
       // Set response success flag to false
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 49f329c..d1c8283 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
@@ -102,6 +102,8 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
   public static final Logger LOG =
       LoggerFactory.getLogger(OMTenantCreateRequest.class);
 
+  private transient Tenant tenantInContext;
+
   public OMTenantCreateRequest(OMRequest omRequest) {
     super(omRequest);
   }
@@ -159,11 +161,12 @@ public class OMTenantCreateRequest extends 
OMVolumeRequest {
 
     // If we fail after pre-execute. handleRequestFailure() callback
     // would clean up any state maintained by the getMultiTenantManager.
-    final Tenant tenant =
-        ozoneManager.getMultiTenantManager().createTenant(tenantName);
+    tenantInContext = ozoneManager.getMultiTenantManager()
+        .createTenantAccessInAuthorizer(tenantName);
 
     // Get the tenant default policy, pass this along
-    final String tenantDefaultPolicies = tenant.getTenantAccessPolicies()
+    final String tenantDefaultPolicies = tenantInContext
+        .getTenantAccessPolicies()
         .stream().map(AccessPolicy::getPolicyID)
         .collect(Collectors.joining(","));
 
@@ -187,14 +190,11 @@ public class OMTenantCreateRequest extends 
OMVolumeRequest {
 
   @Override
   public void handleRequestFailure(OzoneManager ozoneManager) {
-    final CreateTenantRequest request = 
getOmRequest().getCreateTenantRequest();
-
     try {
-      final Tenant tenant = ozoneManager.getMultiTenantManager()
-          .getTenantInfo(request.getTenantName());
       // Cleanup any state maintained by OMMultiTenantManager
-      if (tenant != null) {
-        ozoneManager.getMultiTenantManager().destroyTenant(tenant);
+      if (tenantInContext != null) {
+        ozoneManager.getMultiTenantManager()
+            .removeTenantAccessFromAuthorizer(tenantInContext);
       }
     } catch (Exception e) {
       // TODO: Ignore for now. Multi-Tenant Manager is responsible for
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 532b268..07b2b89 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
@@ -45,6 +45,7 @@ import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
 import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
 import org.apache.hadoop.ozone.om.helpers.TenantInfoList;
 import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
 import org.apache.hadoop.ozone.om.ratis.OzoneManagerDoubleBuffer;
 import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
 import org.apache.hadoop.ozone.om.request.OMClientRequest;
@@ -88,6 +89,8 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3Vo
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantGetUserInfoRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantGetUserInfoResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantListUserRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantListUserResponse;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
 import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
 
@@ -244,6 +247,11 @@ public class OzoneManagerRequestHandler implements 
RequestHandler {
             request.getListTenantRequest());
         responseBuilder.setListTenantResponse(listTenantResponse);
         break;
+      case TenantListUser:
+        TenantListUserResponse listUserResponse = tenantListUsers(
+            request.getTenantListUserRequest());
+        responseBuilder.setTenantListUserResponse(listUserResponse);
+        break;
       default:
         responseBuilder.setSuccess(false);
         responseBuilder.setMessage("Unrecognized Command Type: " + cmdType);
@@ -386,6 +394,22 @@ public class OzoneManagerRequestHandler implements 
RequestHandler {
     return resp.build();
   }
 
+  private TenantListUserResponse tenantListUsers(
+      TenantListUserRequest request) throws IOException {
+    TenantListUserResponse.Builder builder =
+        TenantListUserResponse.newBuilder();
+    TenantUserList usersInTenant =
+        impl.listUsersInTenant(request.getTenantName(), request.getPrefix());
+    if (usersInTenant == null) {
+      builder.setSuccess(false);
+    } else {
+      builder.setSuccess(true);
+      builder.setTenantName(request.getTenantName());
+      builder.addAllUserAccessIdInfo(usersInTenant.getUserAccessIds());
+    }
+    return builder.build();
+  }
+
   private ListTenantResponse listTenant(
       ListTenantRequest request) throws IOException {
 
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManagerImpl.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManagerImpl.java
new file mode 100644
index 0000000..de87581
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManagerImpl.java
@@ -0,0 +1,138 @@
+/*
+ * 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 static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DB_DIRS;
+import static 
org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBTenantInfo;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
+import org.apache.http.auth.BasicUserPrincipal;
+import org.apache.ozone.test.LambdaTestUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests for Multi Tenant Manager APIs.
+ */
+public class TestOMMultiTenantManagerImpl {
+
+  private OMMultiTenantManagerImpl tenantManager;
+  private static String tenantName = "tenant1";
+
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
+  @Before
+  public void setUp() throws IOException {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(OZONE_OM_DB_DIRS,
+        folder.newFolder().getAbsolutePath());
+    conf.set(OZONE_OM_TENANT_DEV_SKIP_RANGER, "true");
+    OMMetadataManager omMetadataManager = new OmMetadataManagerImpl(conf);
+
+    final String bucketNamespaceName = tenantName;
+    final String accountNamespaceName = tenantName;
+    final String userPolicyGroupName =
+        tenantName + OzoneConsts.DEFAULT_TENANT_USER_POLICY_SUFFIX;
+    final String bucketPolicyGroupName =
+        tenantName + OzoneConsts.DEFAULT_TENANT_BUCKET_POLICY_SUFFIX;
+
+    final OmDBTenantInfo omDBTenantInfo = new OmDBTenantInfo(
+        tenantName, bucketNamespaceName, accountNamespaceName,
+        userPolicyGroupName, bucketPolicyGroupName);
+    omMetadataManager.getTenantStateTable().put(tenantName, omDBTenantInfo);
+
+    omMetadataManager.getTenantAccessIdTable().put("seed-accessId1",
+        new OmDBAccessIdInfo(tenantName, "seed-user1",
+            "sharedsecret1", false, false));
+
+    tenantManager = new OMMultiTenantManagerImpl(omMetadataManager, conf);
+    assertEquals(1, tenantManager.getTenantCache().size());
+    assertEquals(1,
+        
tenantManager.getTenantCache().get(tenantName).getTenantUsers().size());
+
+  }
+
+  @Test
+  public void testListUsersInTenant() throws Exception {
+    tenantManager.assignUserToTenant(new BasicUserPrincipal("user1"),
+        tenantName, "accessId1");
+
+    TenantUserList tenantUserList =
+        tenantManager.listUsersInTenant(tenantName, "");
+    List<TenantUserAccessId> userAccessIds = tenantUserList.getUserAccessIds();
+    assertEquals(2, userAccessIds.size());
+
+    for (TenantUserAccessId userAccessId : userAccessIds) {
+      String user = userAccessId.getUser();
+      if (user.equals("user1")) {
+        assertEquals("accessId1", userAccessId.getAccessId());
+      } else if (user.equals("seed-user1")) {
+        assertEquals("seed-accessId1", userAccessId.getAccessId());
+      } else {
+        Assert.fail();
+      }
+    }
+
+    LambdaTestUtils.intercept(IOException.class,
+        "Tenant 'tenant2' not found", () -> {
+          tenantManager.listUsersInTenant("tenant2", null);
+        });
+
+    assertTrue(tenantManager.listUsersInTenant(tenantName, "abc")
+        .getUserAccessIds().isEmpty());
+  }
+
+  @Test
+  public void testRevokeUserAccessId() throws Exception {
+
+    LambdaTestUtils.intercept(OMException.class, () ->
+        tenantManager.revokeUserAccessId("accessId1"));
+    assertEquals(1, tenantManager.getTenantCache().size());
+
+    tenantManager.revokeUserAccessId("seed-accessId1");
+    assertTrue(tenantManager.getTenantCache()
+        .get(tenantName).getTenantUsers().isEmpty());
+    assertTrue(tenantManager.listUsersInTenant(tenantName, null)
+        .getUserAccessIds().isEmpty());
+  }
+
+  @Test
+  public void testGetTenantForAccessID() throws Exception {
+    assertEquals(tenantName, tenantManager.getTenantForAccessID("seed" +
+        "-accessId1"));
+    LambdaTestUtils.intercept(OMException.class, () -> {
+      tenantManager.getTenantForAccessID("invalid-accessId1");
+    });
+  }
+}
\ No newline at end of file
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
index 2870bb0..42906fa 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyAclRequestWithFSO.java
@@ -24,7 +24,7 @@ import 
org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
 import org.apache.hadoop.ozone.om.request.TestOMRequestUtils;
 import org.apache.hadoop.ozone.om.request.key.acl.OMKeyAclRequest;
 import org.apache.hadoop.ozone.om.request.key.acl.OMKeyAddAclRequestWithFSO;
-import 
org.apache.hadoop.ozone.om.request.key.acl.OMKeyRemoveAclRequestWithFSO;;
+import org.apache.hadoop.ozone.om.request.key.acl.OMKeyRemoveAclRequestWithFSO;
 import org.apache.hadoop.ozone.om.request.key.acl.OMKeySetAclRequestWithFSO;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 import org.apache.hadoop.util.Time;
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 4a47251..cdc1132 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
@@ -133,11 +133,11 @@ public class TestS3GetSecretRequest {
     // Multi-tenant related initializations
     omMultiTenantManager = mock(OMMultiTenantManager.class);
     tenant = mock(Tenant.class);
-    when(omMultiTenantManager.getTenantInfo(TENANT_NAME)).thenReturn(tenant);
     
when(ozoneManager.getMultiTenantManager()).thenReturn(omMultiTenantManager);
 
     when(tenant.getTenantAccessPolicies()).thenReturn(new ArrayList<>());
-    when(omMultiTenantManager.createTenant(TENANT_NAME)).thenReturn(tenant);
+    when(omMultiTenantManager.createTenantAccessInAuthorizer(TENANT_NAME))
+        .thenReturn(tenant);
   }
 
   @After
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListUsersHandler.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListUsersHandler.java
new file mode 100644
index 0000000..458a458
--- /dev/null
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantListUsersHandler.java
@@ -0,0 +1,71 @@
+/*
+ * 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.shell.tenant;
+
+import java.io.IOException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.om.helpers.TenantUserList;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserAccessId;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import org.apache.hadoop.ozone.shell.s3.S3Handler;
+
+import picocli.CommandLine;
+
+/**
+ * Command to list users in a tenant along with corresponding accessId.
+ */
[email protected](name = "list",
+    description = "List Tenant Users")
+public class TenantListUsersHandler extends S3Handler {
+
+  @CommandLine.Spec
+  private CommandLine.Model.CommandSpec spec;
+
+  @CommandLine.Option(names = {"-t", "--tenant"},
+      description = "Tenant name")
+  private String tenantName;
+
+  @CommandLine.Option(names = {"-p", "--prefix"},
+      description = "Filter users with this prefix.")
+  private String prefix;
+
+  @Override
+  protected void execute(OzoneClient client, OzoneAddress address) {
+    final ObjectStore objStore = client.getObjectStore();
+
+    if (StringUtils.isEmpty(tenantName)) {
+      err().println("Please specify a tenant name with -t.");
+      return;
+    }
+    try {
+      TenantUserList usersInTenant =
+          objStore.listUsersInTenant(tenantName, prefix);
+      for (TenantUserAccessId accessIdInfo : usersInTenant.getUserAccessIds()) 
{
+        out().println("- User '" + accessIdInfo.getUser() +
+            "' with accessId '" + accessIdInfo.getAccessId() + "'");
+      }
+    } catch (IOException e) {
+      err().println("Failed to Get Users in tenant '" + tenantName
+          + "': " + e.getMessage());
+    }
+  }
+}
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
index 591d3b0..356fafa 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
@@ -39,7 +39,8 @@ import java.util.concurrent.Callable;
         TenantAssignUserAccessIdHandler.class,
         TenantRevokeUserAccessIdHandler.class,
         TenantAssignAdminHandler.class,
-        TenantRevokeAdminHandler.class
+        TenantRevokeAdminHandler.class,
+        TenantListUsersHandler.class
     },
     mixinStandardHelpOptions = true,
     versionProvider = HddsVersionProvider.class)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to