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 2c95164  HDDS-5647. [Multi-Tenant] Implement AssignUserToTenant (#2564)
2c95164 is described below

commit 2c95164bf2be7728134499594e2a8007c827ece4
Author: Siyao Meng <[email protected]>
AuthorDate: Wed Sep 15 11:16:37 2021 -0700

    HDDS-5647. [Multi-Tenant] Implement AssignUserToTenant (#2564)
---
 .../apache/hadoop/ozone/client/ObjectStore.java    |  11 +-
 .../ozone/client/protocol/ClientProtocol.java      |   9 +-
 .../apache/hadoop/ozone/client/rpc/RpcClient.java  |  20 ++-
 hadoop-ozone/common/pom.xml                        |   4 -
 .../main/java/org/apache/hadoop/ozone/OmUtils.java |   2 +-
 .../org/apache/hadoop/ozone/audit/OMAction.java    |   2 +-
 .../hadoop/ozone/om/helpers/OmDBAccessIdInfo.java  |   3 -
 .../om/helpers/OmDBKerberosPrincipalInfo.java      |   3 -
 .../hadoop/ozone/om/multitenant/AccessPolicy.java  |   4 +-
 .../MultiTenantAccessAuthorizerRangerPlugin.java   |  75 ++++-----
 .../ozone/om/multitenant/RangerAccessPolicy.java   |   6 +-
 .../ozone/om/protocol/OzoneManagerProtocol.java    |   9 +-
 ...OzoneManagerProtocolClientSideTranslatorPB.java |  22 +--
 .../main/compose/ozonesecure/docker-compose.yaml   |  16 +-
 .../src/main/compose/ozonesecure/docker-config     |   3 +
 .../ozonesecure/mockserverInitialization.json      |  42 +++++
 .../hadoop/ozone/shell/TestOzoneShellHA.java       |  21 +++
 .../src/main/proto/OmClientProtocol.proto          |  13 +-
 .../hadoop/ozone/om/OMMultiTenantManager.java      |   3 +-
 .../hadoop/ozone/om/OMMultiTenantManagerImpl.java  |   2 +-
 .../hadoop/ozone/om/OmMetadataManagerImpl.java     |  17 +-
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |   8 +-
 .../om/ratis/utils/OzoneManagerRatisUtils.java     |   6 +-
 ...quest.java => OMAssignUserToTenantRequest.java} | 177 ++++++++++++++-------
 ...onse.java => OMAssignUserToTenantResponse.java} |  47 ++++--
 .../ozone/shell/s3/AssignUserToTenantHandler.java  | 126 +++++++++++++++
 .../hadoop/ozone/shell/s3/TenantCreateHandler.java |   2 +-
 .../hadoop/ozone/shell/s3/TenantUserCommands.java  |   2 +-
 .../ozone/shell/s3/TenantUserCreateHandler.java    |  70 --------
 29 files changed, 470 insertions(+), 255 deletions(-)

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 1550136..72b82d6 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
@@ -188,14 +188,15 @@ public class ObjectStore {
   // TODO: modify, delete
 
   /**
-   * Create tenant user.
-   * @param tenantUsername tenant user name.
+   * Assign user to tenant.
+   * @param username user name to be assigned.
    * @param tenantName tenant name.
+   * @param accessId access ID.
    * @throws IOException
    */
-  public S3SecretValue createTenantUser(
-      String tenantUsername, String tenantName) throws IOException {
-    return proxy.createTenantUser(tenantUsername, tenantName);
+  public S3SecretValue assignUserToTenant(
+      String username, String tenantName, String accessId) throws IOException {
+    return proxy.assignUserToTenant(username, tenantName, accessId);
   }
 
   // TODO: modify, delete
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 318c2dc..2676710 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
@@ -573,13 +573,14 @@ public interface ClientProtocol {
 //  void deleteTenant(String tenantName) throws IOException;
 
   /**
-   * Create tenant user.
-   * @param tenantUsername tenant user name.
+   * Assign user to tenant.
+   * @param username user name to be assigned.
    * @param tenantName tenant name.
+   * @param accessId access ID.
    * @throws IOException
    */
-  S3SecretValue createTenantUser(String tenantUsername, String tenantName)
-      throws IOException;
+  S3SecretValue assignUserToTenant(String username, String tenantName,
+      String accessId) throws IOException;
 
   // TODO: modify, delete
 
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 2495abd..53fd407 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
@@ -637,18 +637,22 @@ public class RpcClient implements ClientProtocol {
   // TODO: modify, delete
 
   /**
-   * Create tenant user.
-   * @param tenantUsername tenant user name.
+   * Assign user to tenant.
+   * @param username user name to be assigned.
+   * @param tenantName tenant name.
    * @throws IOException
    */
   @Override
-  public S3SecretValue createTenantUser(
-      String tenantUsername, String tenantName) throws IOException {
-    Preconditions.checkArgument(Strings.isNotBlank(tenantUsername),
-        "tenantUsername cannot be null or empty.");
+  public S3SecretValue assignUserToTenant(
+      String username, String tenantName, String accessId) throws IOException {
+    Preconditions.checkArgument(Strings.isNotBlank(username),
+        "username can't be null or empty.");
     Preconditions.checkArgument(Strings.isNotBlank(tenantName),
-        "tenantName cannot be null or empty.");
-    return ozoneManagerClient.createTenantUser(tenantUsername, tenantName);
+        "tenantName can't be null or empty.");
+    Preconditions.checkArgument(Strings.isNotBlank(accessId),
+        "accessId can't be null or empty.");
+    return ozoneManagerClient.assignUserToTenant(
+        username, tenantName, accessId);
   }
 
   // TODO: modify, delete
diff --git a/hadoop-ozone/common/pom.xml b/hadoop-ozone/common/pom.xml
index c994932..80407da 100644
--- a/hadoop-ozone/common/pom.xml
+++ b/hadoop-ozone/common/pom.xml
@@ -80,10 +80,6 @@ https://maven.apache.org/xsd/maven-4.0.0.xsd";>
       <groupId>commons-net</groupId>
       <artifactId>commons-net</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.codehaus.jettison</groupId>
-      <artifactId>jettison</artifactId>
-    </dependency>
   </dependencies>
 
   <build>
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 3d33d39..8dc4bfc 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
@@ -303,7 +303,7 @@ public final class OmUtils {
     case CreateTenant:
     case ModifyTenant:
     case DeleteTenant:
-    case CreateTenantUser:
+    case AssignUserToTenant:
     case ModifyTenantUser:
     case DeleteTenantUser:
       return false;
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 2aad4d6..390bbb5 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
@@ -77,7 +77,7 @@ public enum OMAction implements AuditAction {
   MODIFY_TENANT,
   DELETE_TENANT,
 
-  CREATE_TENANT_USER,
+  ASSIGN_USER_TO_TENANT,
   MODIFY_TENANT_USER,
   DELETE_TENANT_USER;
 
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java
index 0676c05..a9c5366 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBAccessIdInfo.java
@@ -102,9 +102,6 @@ public final class OmDBAccessIdInfo {
     private String kerberosPrincipal;
     private String sharedSecret;
 
-    private Builder() {
-    }
-
     public Builder setTenantName(String tenantId) {
       this.tenantId = tenantId;
       return this;
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
index 8721706..e9beec2 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
@@ -87,9 +87,6 @@ public final class OmDBKerberosPrincipalInfo {
   public static final class Builder {
     private Set<String> accessIds;
 
-    private Builder() {
-    }
-
     public Builder setAccessIds(Set<String> accessIds) {
       this.accessIds = accessIds;
       return this;
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccessPolicy.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccessPolicy.java
index 1b3f2bd..346a848 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccessPolicy.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/AccessPolicy.java
@@ -23,7 +23,7 @@ import org.apache.hadoop.hdds.annotation.InterfaceAudience;
 import org.apache.hadoop.hdds.annotation.InterfaceStability;
 import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
-import org.codehaus.jettison.json.JSONObject;
+import com.google.gson.JsonObject;
 
 /**
  * AccessPolicy interface for Ozone Multi-Tenancy.
@@ -113,7 +113,7 @@ public interface AccessPolicy {
    * @return
    * @throws Exception
    */
-  String deserializePolicyFromJsonString(JSONObject jsonObject)
+  String deserializePolicyFromJsonString(JsonObject jsonObject)
       throws Exception;
 
   /**
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java
index 29efabe..86c44f6 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerRangerPlugin.java
@@ -51,6 +51,10 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
 
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.commons.net.util.Base64;
@@ -59,9 +63,6 @@ import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.security.acl.IOzoneObj;
 import org.apache.hadoop.ozone.security.acl.RequestContext;
 import 
org.apache.hadoop.security.authentication.client.AuthenticationException;
-import org.codehaus.jettison.json.JSONArray;
-import org.codehaus.jettison.json.JSONException;
-import org.codehaus.jettison.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -229,21 +230,22 @@ public class MultiTenantAccessAuthorizerRangerPlugin 
implements
 
     HttpsURLConnection conn = makeHttpsGetCall(rangerAdminUrl,
         "GET", false);
-    String response = getReponseData(conn);
+    String response = getResponseData(conn);
     String groupIDCreated = null;
     try {
-      JSONObject jResonse = new JSONObject(response);
-      JSONArray info = jResonse.getJSONArray("vXGroups");
-      int numIndex = info.length();
+      JsonObject jResonse = new JsonParser().parse(response).getAsJsonObject();
+      JsonArray info = jResonse.get("vXGroups").getAsJsonArray();
+      int numIndex = info.size();
       for (int i = 0; i < numIndex; ++i) {
-        if (info.getJSONObject(i).getString("name")
+        if (info.get(i).getAsJsonObject().get("name").getAsString()
             .equals(principal.getFullMultiTenantPrincipalID())) {
-          groupIDCreated = info.getJSONObject(i).getString("id");
+          groupIDCreated =
+              info.get(i).getAsJsonObject().get("id").getAsString();
           break;
         }
       }
       System.out.println("Group ID is : " + groupIDCreated);
-    } catch (JSONException e) {
+    } catch (JsonParseException e) {
       e.printStackTrace();
       throw e;
     }
@@ -259,21 +261,22 @@ public class MultiTenantAccessAuthorizerRangerPlugin 
implements
 
     HttpsURLConnection conn = makeHttpsGetCall(rangerAdminUrl,
         "GET", false);
-    String response = getReponseData(conn);
+    String response = getResponseData(conn);
     String userIDCreated = null;
     try {
-      JSONObject jResonse = new JSONObject(response);
-      JSONArray userinfo = jResonse.getJSONArray("vXUsers");
-      int numIndex = userinfo.length();
+      JsonObject jResonse = new JsonParser().parse(response).getAsJsonObject();
+      JsonArray userinfo = jResonse.get("vXUsers").getAsJsonArray();
+      int numIndex = userinfo.size();
       for (int i = 0; i < numIndex; ++i) {
-        if (userinfo.getJSONObject(i).getString("name")
+        if (userinfo.get(i).getAsJsonObject().get("name").getAsString()
             .equals(principal.getFullMultiTenantPrincipalID())) {
-          userIDCreated = userinfo.getJSONObject(i).getString("id");
+          userIDCreated =
+              userinfo.get(i).getAsJsonObject().get("id").getAsString();
           break;
         }
       }
       System.out.println("User ID is : " + userIDCreated);
-    } catch (JSONException e) {
+    } catch (JsonParseException e) {
       e.printStackTrace();
       throw e;
     }
@@ -291,13 +294,13 @@ public class MultiTenantAccessAuthorizerRangerPlugin 
implements
 
     HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
         jsonCreateUserString, "POST", false);
-    String userInfo = getReponseData(conn);
+    String userInfo = getResponseData(conn);
     String userIDCreated;
     try {
-      JSONObject jObject = new JSONObject(userInfo.toString());
-      userIDCreated = jObject.getString("id");
+      JsonObject jObject = new JsonParser().parse(userInfo).getAsJsonObject();
+      userIDCreated = jObject.get("id").getAsString();
       System.out.println("User ID is : " + userIDCreated);
-    } catch (JSONException e) {
+    } catch (JsonParseException e) {
       e.printStackTrace();
       throw e;
     }
@@ -322,13 +325,13 @@ public class MultiTenantAccessAuthorizerRangerPlugin 
implements
     HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
         jsonCreateGroupString,
         "POST", false);
-    String groupInfo = getReponseData(conn);
+    String groupInfo = getResponseData(conn);
     String groupIdCreated;
     try {
-      JSONObject jObject = new JSONObject(groupInfo.toString());
-      groupIdCreated = jObject.getString("id");
-      System.out.println("GroupID is : " + groupIdCreated);
-    } catch (JSONException e) {
+      JsonObject jObject = new JsonParser().parse(groupInfo).getAsJsonObject();
+      groupIdCreated = jObject.get("id").getAsString();
+      System.out.println("GroupID is: " + groupIdCreated);
+    } catch (JsonParseException e) {
       e.printStackTrace();
       throw e;
     }
@@ -342,13 +345,13 @@ public class MultiTenantAccessAuthorizerRangerPlugin 
implements
     HttpsURLConnection conn = makeHttpsPostCall(rangerAdminUrl,
         policy.serializePolicyToJsonString(),
         "POST", false);
-    String policyInfo = getReponseData(conn);
+    String policyInfo = getResponseData(conn);
     String policyID;
     try {
-      JSONObject jObject = new JSONObject(policyInfo.toString());
-      policyID = jObject.getString("id");
+      JsonObject jObject = new 
JsonParser().parse(policyInfo).getAsJsonObject();
+      policyID = jObject.get("id").getAsString();
       System.out.println("policyID is : " + policyID);
-    } catch (JSONException e) {
+    } catch (JsonParseException e) {
       e.printStackTrace();
       throw e;
     }
@@ -363,9 +366,9 @@ public class MultiTenantAccessAuthorizerRangerPlugin 
implements
 
     HttpsURLConnection conn = makeHttpsGetCall(rangerAdminUrl,
         "GET", false);
-    String policyInfo = getReponseData(conn);
-    JSONArray jArry = new JSONArray(policyInfo);
-    JSONObject jsonObject = jArry.getJSONObject(0);
+    String policyInfo = getResponseData(conn);
+    JsonArray jArry = new JsonParser().parse(policyInfo).getAsJsonArray();
+    JsonObject jsonObject = jArry.get(0).getAsJsonObject();
     AccessPolicy policy = new RangerAccessPolicy(policyName);
     policy.deserializePolicyFromJsonString(jsonObject);
     return policy;
@@ -424,16 +427,16 @@ public class MultiTenantAccessAuthorizerRangerPlugin 
implements
     }
   }
 
-  private String getReponseData(HttpsURLConnection urlConnection)
+  private String getResponseData(HttpsURLConnection urlConnection)
       throws IOException {
     StringBuilder response = new StringBuilder();
     try (BufferedReader br = new BufferedReader(
         new InputStreamReader(urlConnection.getInputStream(), "utf-8"))) {
-      String responseLine = null;
+      String responseLine;
       while ((responseLine = br.readLine()) != null) {
         response.append(responseLine.trim());
       }
-      System.out.println(response.toString());
+      System.out.println(response);
     } catch (Exception e) {
       e.printStackTrace();
       throw e;
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
index c7bcd54..f2ca425 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/RangerAccessPolicy.java
@@ -19,7 +19,7 @@ package org.apache.hadoop.ozone.om.multitenant;
 
 import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
-import org.codehaus.jettison.json.JSONObject;
+import com.google.gson.JsonObject;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -67,9 +67,9 @@ public class RangerAccessPolicy implements AccessPolicy {
   }
 
   @Override
-  public String deserializePolicyFromJsonString(JSONObject jsonObject)
+  public String deserializePolicyFromJsonString(JsonObject jsonObject)
       throws Exception {
-    setPolicyID(jsonObject.getString("id"));
+    setPolicyID(jsonObject.get("id").getAsString());
     // TODO : retrieve other policy fields as well.
     return null;
   }
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 c1e97fe..db1e373 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
@@ -465,13 +465,14 @@ public interface OzoneManagerProtocol
   // TODO: modify, delete
 
   /**
-   * Create tenant user.
-   * @param tenantUsername tenant user name.
+   * Assign user to tenant.
+   * @param username user name to be assigned.
    * @param tenantName tenant name.
+   * @param accessId access ID.
    * @throws IOException
    */
-  S3SecretValue createTenantUser(String tenantUsername, String tenantName)
-      throws IOException;
+  S3SecretValue assignUserToTenant(String username, String tenantName,
+      String accessId) throws IOException;
 
   // TODO: modify, delete
 
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 69967db..d21e532 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
@@ -73,8 +73,8 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateF
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateKeyRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateKeyResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
-import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserRequest;
-import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesResponse;
@@ -902,19 +902,21 @@ public final class 
OzoneManagerProtocolClientSideTranslatorPB
    * {@inheritDoc}
    */
   @Override
-  public S3SecretValue createTenantUser(
-      String tenantUsername, String tenantName) throws IOException {
+  public S3SecretValue assignUserToTenant(
+      String username, String tenantName, String accessId) throws IOException {
 
-    final CreateTenantUserRequest request = 
CreateTenantUserRequest.newBuilder()
-        .setTenantUsername(tenantUsername)
+    final AssignUserToTenantRequest request =
+        AssignUserToTenantRequest.newBuilder()
+        .setTenantUsername(username)
         .setTenantName(tenantName)
+        .setAccessId(accessId)
         .build();
-    final OMRequest omRequest = createOMRequest(Type.CreateTenantUser)
-        .setCreateTenantUserRequest(request)
+    final OMRequest omRequest = createOMRequest(Type.AssignUserToTenant)
+        .setAssignUserToTenantRequest(request)
         .build();
     final OMResponse omResponse = submitRequest(omRequest);
-    final CreateTenantUserResponse resp = handleError(omResponse)
-        .getCreateTenantUserResponse();
+    final AssignUserToTenantResponse resp = handleError(omResponse)
+        .getAssignUserToTenantResponse();
 
     return S3SecretValue.fromProtobuf(resp.getS3Secret());
   }
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-compose.yaml 
b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-compose.yaml
index cfd0de8..b0d19cc 100644
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-compose.yaml
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-compose.yaml
@@ -59,7 +59,7 @@ services:
       - 9862:9862
     environment:
       ENSURE_OM_INITIALIZED: /data/metadata/om/current/VERSION
-      OZONE_OPTS:
+      OZONE_OPTS: -Dcom.sun.net.ssl.checkRevocation=false
     env_file:
       - docker-config
     command: ["/opt/hadoop/bin/ozone","om"]
@@ -109,3 +109,17 @@ services:
       OZONE-SITE.XML_hdds.scm.safemode.min.datanode: 
"${OZONE_SAFEMODE_MIN_DATANODES:-1}"
       OZONE_OPTS:
     command: ["/opt/hadoop/bin/ozone","scm"]
+  ranger:
+    image: mockserver/mockserver:mockserver-5.11.2
+    hostname: ranger
+    volumes:
+      - ./mockserverInitialization.json:/config/mockserverInitialization.json
+    ports:
+      - 6182:6182
+    environment:
+      MOCKSERVER_MAX_EXPECTATIONS: 100
+      MOCKSERVER_MAX_HEADER_SIZE: 8192
+      MOCKSERVER_WATCH_INITIALIZATION_JSON: "true"
+      MOCKSERVER_INITIALIZATION_JSON_PATH: 
/config/mockserverInitialization.json
+    command: -logLevel DEBUG -serverPort 6182
+
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config 
b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
index 103a997..4c4f023 100644
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure/docker-config
@@ -124,3 +124,6 @@ OZONE_CONF_DIR=/etc/hadoop
 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
+
diff --git 
a/hadoop-ozone/dist/src/main/compose/ozonesecure/mockserverInitialization.json 
b/hadoop-ozone/dist/src/main/compose/ozonesecure/mockserverInitialization.json
new file mode 100644
index 0000000..e1142bc
--- /dev/null
+++ 
b/hadoop-ozone/dist/src/main/compose/ozonesecure/mockserverInitialization.json
@@ -0,0 +1,42 @@
+[
+  {
+    "httpRequest": {
+      "path": "/"
+    },
+    "httpResponse": {
+      "body": "{}"
+    }
+  },
+  {
+    "httpRequest": {
+      "path": "/service/xusers/secure/users*"
+    },
+    "httpResponse": {
+      "body": "{id: 111}"
+    }
+  },
+  {
+    "httpRequest": {
+      "path": "/service/xusers/secure/groups"
+    },
+    "httpResponse": {
+      "body": "{id: 222}"
+    }
+  },
+  {
+    "httpRequest": {
+      "path": "/service/public/v2/api/policy"
+    },
+    "httpResponse": {
+      "body": "{id: 333}"
+    }
+  },
+  {
+    "httpRequest": {
+      "path": "/service/public/v2/api/policy/*",
+    },
+    "httpResponse": {
+      "body": "[{id: 444}]"
+    }
+  }
+]
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
index b32ff32..213b6c7 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
@@ -42,6 +42,7 @@ import org.apache.hadoop.ozone.client.ObjectStore;
 import org.apache.hadoop.ozone.ha.ConfUtils;
 import org.apache.hadoop.ozone.om.OMConfigKeys;
 import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.shell.s3.S3Shell;
 import org.apache.ozone.test.GenericTestUtils;
 import org.apache.ozone.test.LambdaTestUtils;
 import org.apache.hadoop.util.ToolRunner;
@@ -95,6 +96,7 @@ public class TestOzoneShellHA {
   private static MiniOzoneCluster cluster = null;
   private static OzoneShell ozoneShell = null;
   private static OzoneAdmin ozoneAdminShell = null;
+  private static S3Shell s3Shell = null;
 
   private final ByteArrayOutputStream out = new ByteArrayOutputStream();
   private final ByteArrayOutputStream err = new ByteArrayOutputStream();
@@ -127,6 +129,7 @@ public class TestOzoneShellHA {
 
     ozoneShell = new OzoneShell();
     ozoneAdminShell = new OzoneAdmin();
+    s3Shell = new S3Shell();
 
     // Init HA cluster
     omServiceId = "om-service-test1";
@@ -855,4 +858,22 @@ public class TestOzoneShellHA {
     objectStore.getVolume("vol4").deleteBucket("buck4");
     objectStore.deleteVolume("vol4");
   }
+
+  /**
+   * Test ozone tenant commands.
+   */
+  @Test
+  public void testOzoneTenant() {
+    // TODO: tenant subcommand will be moved from s3 to admin later.
+
+    // Test create tenant
+    execute(s3Shell, new String[] {
+        "tenant", "create", "finance",
+        "--om-service-id=" + omServiceId});
+
+    // Test assign user
+    execute(s3Shell, new String[] {
+        "user", "assign", "[email protected]", "--tenant=finance",
+        "--om-service-id=" + omServiceId});
+  }
 }
diff --git 
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto 
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index e89183a..62fec08 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -107,7 +107,7 @@ enum Type {
   ModifyTenant = 96;
   DeleteTenant = 97;
 
-  CreateTenantUser = 98;
+  AssignUserToTenant = 98;
   ModifyTenantUser = 99;
   DeleteTenantUser = 100;
 }
@@ -197,7 +197,7 @@ message OMRequest {
   optional ModifyTenantRequest              ModifyTenantRequest            = 
96;
   optional DeleteTenantRequest              DeleteTenantRequest            = 
97;
 
-  optional CreateTenantUserRequest          CreateTenantUserRequest        = 
98;
+  optional AssignUserToTenantRequest        AssignUserToTenantRequest      = 
98;
   optional ModifyTenantUserRequest          ModifyTenantUserRequest        = 
99;
   optional DeleteTenantUserRequest          DeleteTenantUserRequest        = 
100;
 }
@@ -282,8 +282,10 @@ message OMResponse {
   optional ModifyTenantResponse              ModifyTenantResponse          = 
96;
   optional DeleteTenantResponse              DeleteTenantResponse          = 
97;
 
-  optional CreateTenantUserResponse          CreateTenantUserResponse      = 
98;
+  optional AssignUserToTenantResponse        AssignUserToTenantResponse    = 
98;
+  // TODO: Remove ModifyTenantUserResponse since it won't be useful anymore?
   optional ModifyTenantUserResponse          ModifyTenantUserResponse      = 
99;
+  // TODO: Rename this to UnassignUserFromTenant Response ?
   optional DeleteTenantUserResponse          DeleteTenantUserResponse      = 
100;
 }
 
@@ -1379,9 +1381,10 @@ message DeleteTenantRequest {
     optional string tenantName = 1;
 }
 
-message CreateTenantUserRequest {
+message AssignUserToTenantRequest {
     optional string tenantUsername = 1;
     optional string tenantName = 2;
+    optional string accessId = 3;
 }
 
 message ModifyTenantUserRequest {
@@ -1404,7 +1407,7 @@ message DeleteTenantResponse {
     optional bool success = 1;
 }
 
-message CreateTenantUserResponse {
+message AssignUserToTenantResponse {
     optional bool success = 1;
     optional S3Secret s3Secret = 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 859f43a..d9eacdf 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
@@ -111,12 +111,13 @@ public interface OMMultiTenantManager {
 
   /**
    * Creates a new user that exists for S3 API access to Ozone.
+   * TODO: FIX the description.
    * @param tenantName
    * @param userName
    * @return Unique UserID.
    * @throws IOException if there is any error condition detected.
    */
-  String createUser(String tenantName, String userName);
+  String assignUserToTenant(String tenantName, String userName);
 
   /**
    * Given a user, destroys all state associated with that user.
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 0eb01dc..a9e0962 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
@@ -307,7 +307,7 @@ public class OMMultiTenantManagerImpl implements 
OMMultiTenantManager {
    * @return Tenant, or null on error
    */
   @Override
-  public String createUser(String tenantName, String userName) {
+  public String assignUserToTenant(String tenantName, String userName) {
     try {
       controlPathLock.writeLock().lock();
       Tenant tenant = getTenantInfo(tenantName);
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
index 6c81c93..69ef912 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
@@ -49,6 +49,8 @@ import org.apache.hadoop.ozone.OzoneConsts;
 import org.apache.hadoop.ozone.common.BlockGroup;
 import org.apache.hadoop.hdds.utils.TransactionInfoCodec;
 import org.apache.hadoop.ozone.om.codec.OmBucketInfoCodec;
+import org.apache.hadoop.ozone.om.codec.OmDBAccessIdInfoCodec;
+import org.apache.hadoop.ozone.om.codec.OmDBKerberosPrincipalInfoCodec;
 import org.apache.hadoop.ozone.om.codec.OmDirectoryInfoCodec;
 import org.apache.hadoop.ozone.om.codec.OmKeyInfoCodec;
 import org.apache.hadoop.ozone.om.codec.OmMultipartKeyInfoCodec;
@@ -135,17 +137,17 @@ public class OmMetadataManagerImpl implements 
OMMetadataManager {
    *
    * Multi-Tenant Tables:
    * |----------------------------------------------------------------------|
-   * | tenantAccessIdTable|  accessId -> OmTenantAccessIdInfo               |
+   * | tenantAccessIdTable       | accessId -> OmDBAccessIdInfo             |
    * |----------------------------------------------------------------------|
    * | principalToAccessIdsTable | Principal -> OmDBKerberosPrincipalInfo   |
    * |----------------------------------------------------------------------|
-   * | tenantStateTable   |  accessId -> OmDBTenantInfo                     |
+   * | tenantStateTable          | accessId -> OmDBTenantInfo               |
    * |----------------------------------------------------------------------|
-   * | tenantGroupTable   |  accessId -> [tenant group A, B, ...]           |
+   * | tenantGroupTable          | accessId -> [tenant group A, B, ...]     |
    * |----------------------------------------------------------------------|
-   * | tenantRoleTable    |  accessId -> roles [admin, roleB, ...]          |
+   * | tenantRoleTable           | accessId -> roles [admin, roleB, ...]    |
    * |----------------------------------------------------------------------|
-   * | tenantPolicyTable  |  policyGroup -> [policyId1, policyId2]          |
+   * | tenantPolicyTable         | policyGroup -> [policyId1, policyId2]    |
    * |----------------------------------------------------------------------|
    *
    * Simple Tables:
@@ -480,7 +482,10 @@ public class OmMetadataManagerImpl implements 
OMMetadataManager {
         .addCodec(OmPrefixInfo.class, new OmPrefixInfoCodec())
         .addCodec(TransactionInfo.class, new TransactionInfoCodec())
         .addCodec(OmDirectoryInfo.class, new OmDirectoryInfoCodec())
-        .addCodec(OmDBTenantInfo.class, new OmDBTenantInfoCodec());
+        .addCodec(OmDBTenantInfo.class, new OmDBTenantInfoCodec())
+        .addCodec(OmDBAccessIdInfo.class, new OmDBAccessIdInfoCodec())
+        .addCodec(OmDBKerberosPrincipalInfo.class,
+            new OmDBKerberosPrincipalInfoCodec());
   }
 
   /**
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 336f1ed..af85efb 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
@@ -3063,12 +3063,12 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
   // TODO: modify, delete
 
   /**
-   * Create tenant user.
+   * Assign user to tenant.
    */
-  public S3SecretValue createTenantUser(
-      String tenantUsername, String tenantName) throws IOException {
+  public S3SecretValue assignUserToTenant(
+      String username, String tenantName, String accessId) throws IOException {
     throw new NotImplementedException(
-        "non-Ratis createTenantUser() is not implemented");
+        "non-Ratis assignUserToTenant() is not implemented");
   }
 
   // TODO: modify, delete
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 9a874c9..0a8c3dd 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
@@ -82,7 +82,7 @@ import 
org.apache.hadoop.ozone.om.request.s3.security.S3RevokeSecretRequest;
 import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
 import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantDeleteRequest;
 import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantModifyRequest;
-import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantUserCreateRequest;
+import 
org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
 import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantUserDeleteRequest;
 import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantUserModifyRequest;
 import 
org.apache.hadoop.ozone.om.request.security.OMCancelDelegationTokenRequest;
@@ -264,8 +264,8 @@ public final class OzoneManagerRatisUtils {
       return new OMTenantModifyRequest(omRequest);
     case DeleteTenant:
       return new OMTenantDeleteRequest(omRequest);
-    case CreateTenantUser:
-      return new OMTenantUserCreateRequest(omRequest);
+    case AssignUserToTenant:
+      return new OMAssignUserToTenantRequest(omRequest);
     case ModifyTenantUser:
       return new OMTenantUserModifyRequest(omRequest);
     case DeleteTenantUser:
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserCreateRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
similarity index 59%
rename from 
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserCreateRequest.java
rename to 
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
index 35a66b8..e49b4b6 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserCreateRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
@@ -28,6 +28,8 @@ import org.apache.hadoop.ozone.audit.OMAction;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.om.OzoneManager;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
 import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
 import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
@@ -35,9 +37,9 @@ import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
 import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
 import org.apache.hadoop.ozone.om.response.OMClientResponse;
 import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
-import 
org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantUserCreateResponse;
-import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserRequest;
-import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantUserResponse;
+import 
org.apache.hadoop.ozone.om.response.s3.tenant.OMAssignUserToTenantResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantResponse;
 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.S3Secret;
@@ -46,8 +48,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.TreeSet;
 
 import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.S3_SECRET_LOCK;
 import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
@@ -55,10 +59,10 @@ import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_L
 /*
   Ratis execution flow for OMTenantUserCreate
 
-- Client (UserCreateHandler , etc.)
+- Client (AssignUserToTenantHandler, etc.)
   - Check username validity: ensure no invalid characters
   - Send request to server
-- OMTenantUserCreateRequest
+- OMAssignUserToTenantRequest
   - preExecute (perform checks and init)
     - Check username validity (again), check $
       - If username is invalid, throw exception to client; else continue
@@ -66,60 +70,73 @@ import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_L
   - validateAndUpdateCache (update DB)
     - Permission check (checkACL need to check access key now)
     - Grab VOLUME_LOCK write lock
-    - Check user existence
-      - If user doesn't exist, throw exception to client; else continue
     - Check tenant existence
       - If tenant doesn't exist, throw exception to client; else continue
+    - Check accessId existence
+      - If accessId exists, throw exception to client; else continue
     - Grab S3_SECRET_LOCK write lock
     - S3SecretTable: Flush generated S3 secret
       - Key: TENANTNAME$USERNAME (equivalent to kerberosID)
       - Value: <GENERATED_SECRET>
     - Release S3_SECRET_LOCK write lock
-    - tenantUserTable: New entry
-      - Key: Tenant user name. e.g. finance$bob, s3v$alice
-      - Value: Tenant name. e.g. finance
+    - New entry in tenantAccessIdTable:
+      - Key: New accessId for the user in this tenant. e.g. finance$bob ?
+      - Value: OmDBAccessIdInfo. Has tenantId, kerberosPrincipal, sharedSecret.
+    - New entry or update existing entry in principalToAccessIdsTable:
+      - Key: Kerberos principal of the user.
+      - Value: OmDBKerberosPrincipalInfo. Has accessIds.
     - tenantGroupTable: Add this new user to the default tenant group.
       - Key: finance$bob
       - Value: finance-users
-    - tenantRoleTable: TBD. NoOp for prototype.
+    - tenantRoleTable: TBD. No-op for prototype.
     - Release VOLUME_LOCK write lock
  */
 
 /**
- * Handles OMTenantUserCreate request.
+ * Handles OMAssignUserToTenantRequest.
  */
-public class OMTenantUserCreateRequest extends OMVolumeRequest {
+public class OMAssignUserToTenantRequest extends OMVolumeRequest {
   private static final Logger LOG =
-      LoggerFactory.getLogger(OMTenantUserCreateRequest.class);
+      LoggerFactory.getLogger(OMAssignUserToTenantRequest.class);
 
-  public OMTenantUserCreateRequest(OMRequest omRequest) {
+  public OMAssignUserToTenantRequest(OMRequest omRequest) {
     super(omRequest);
   }
 
   @Override
   public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
-    final CreateTenantUserRequest request =
-        getOmRequest().getCreateTenantUserRequest();
+    final AssignUserToTenantRequest request =
+        getOmRequest().getAssignUserToTenantRequest();
+
+    // Note: Tenant username _is_ the Kerberos principal of the user
     final String tenantUsername = request.getTenantUsername();
     final String tenantName = request.getTenantName();
+    final String accessId = request.getAccessId();
 
-    // Check tenantUsername validity
+    // Check tenantUsername (user's Kerberos principal) validity. TODO: Check
     if (tenantUsername.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
-      throw new OMException("Invalid tenant user name " + tenantUsername +
-          ". Tenant user name should not contain delimiter.",
+      throw new OMException("Invalid tenant username '" + tenantUsername +
+          "'. Tenant username shouldn't contain delimiter.",
           OMException.ResultCodes.INVALID_TENANT_USER_NAME);
     }
-    // Tenant and tenant user existence check won't be performed here
 
-    // Generate S3 secret
-    final String principal = tenantName +
-        OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER + tenantUsername;
+    // Check tenant name validity.
+    if (tenantName.contains(OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER)) {
+      throw new OMException("Invalid tenant name '" + tenantUsername +
+          "'. Tenant name shouldn't contain delimiter.",
+          OMException.ResultCodes.INVALID_TENANT_NAME);
+    }
+
+    // Won't check tenant existence in preExecute.
+    // Won't check Kerberos principal existence at all. TODO: Confirm this.
+
+    // Generate random S3 secret
     final String s3Secret = DigestUtils.sha256Hex(OmUtils.getSHADigest());
 
     final UpdateGetS3SecretRequest updateGetS3SecretRequest =
         UpdateGetS3SecretRequest.newBuilder()
             .setAwsSecret(s3Secret)
-            .setKerberosID(principal).build();
+            .setKerberosID(accessId).build();
 
     final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
         .setUserInfo(getUserInfo())
@@ -135,6 +152,7 @@ public class OMTenantUserCreateRequest extends 
OMVolumeRequest {
   }
 
   @Override
+  @SuppressWarnings("checkstyle:methodlength")
   public OMClientResponse validateAndUpdateCache(
       OzoneManager ozoneManager, long transactionLogIndex,
       OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
@@ -142,19 +160,23 @@ public class OMTenantUserCreateRequest extends 
OMVolumeRequest {
     OMClientResponse omClientResponse = null;
     final OMResponse.Builder omResponse =
         OmResponseUtil.getOMResponseBuilder(getOmRequest());
+
     final UpdateGetS3SecretRequest updateGetS3SecretRequest =
         getOmRequest().getUpdateGetS3SecretRequest();
-    final String principal = updateGetS3SecretRequest.getKerberosID();
+    final String accessId = updateGetS3SecretRequest.getKerberosID();
     final String awsSecret = updateGetS3SecretRequest.getAwsSecret();
+
     boolean acquiredVolumeLock = false;
     boolean acquiredS3SecretLock = false;
     OzoneMultiTenantPrincipal tenantPrincipal = null;
     Map<String, String> auditMap = new HashMap<>();
     OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
-    final CreateTenantUserRequest request =
-        getOmRequest().getCreateTenantUserRequest();
+
+    final AssignUserToTenantRequest request =
+        getOmRequest().getAssignUserToTenantRequest();
     final String tenantName = request.getTenantName();
-    final String tenantUsername = request.getTenantUsername();
+    final String principal = request.getTenantUsername();
+    assert(accessId.equals(request.getAccessId()));
     final String volumeName = tenantName;  // TODO: Configurable
     IOException exception = null;
     String userId = null;
@@ -168,73 +190,104 @@ public class OMTenantUserCreateRequest extends 
OMVolumeRequest {
       acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
           VOLUME_LOCK, volumeName);
 
-      // Check tenant existence in tenantStateTable
+      // Expect tenant existence in tenantStateTable
       if (!omMetadataManager.getTenantStateTable().isExist(tenantName)) {
-        LOG.debug("tenant: {} does not exist", tenantName);
-        throw new OMException("Tenant does not exist",
+        LOG.error("tenant {} doesn't exist", tenantName);
+        throw new OMException("tenant '" + tenantName + "' doesn't exist",
             OMException.ResultCodes.TENANT_NOT_FOUND);
       }
-      // Check user existence in tenantUserTable
-      if (omMetadataManager.getTenantUserTable().isExist(principal)) {
-        LOG.debug("principal: {} already exists", principal);
-        throw new OMException("User already exists in tenant",
+
+      // Expect accessId absence from tenantAccessIdTable
+      if (omMetadataManager.getTenantAccessIdTable().isExist(accessId)) {
+        LOG.error("accessId {} already exists", accessId);
+        throw new OMException("accessId '" + accessId + "' already exists!",
             OMException.ResultCodes.TENANT_USER_ALREADY_EXISTS);
       }
 
-      // Add to S3SecretTable. TODO: dedup S3GetSecretRequest
+      // Add to S3SecretTable.
+      // TODO: dedupe - S3GetSecretRequest
       acquiredS3SecretLock = omMetadataManager.getLock()
           .acquireWriteLock(S3_SECRET_LOCK, principal);
 
-      // Sanity check. principal should not exist in S3SecretTable
-      if (omMetadataManager.getS3SecretTable().isExist(principal)) {
-        LOG.error("Unexpected '{}' entry in S3SecretTable", principal);
-        throw new OMException("Unexpected principal entry in S3SecretTable",
+      // Expect accessId absence from S3SecretTable
+      // TODO: This table might be merged with tenantAccessIdTable later.
+      if (omMetadataManager.getS3SecretTable().isExist(accessId)) {
+        LOG.error("accessId '{}' already exists in S3SecretTable", accessId);
+        throw new OMException("accessId '" + accessId +
+            "' already exists in S3SecretTable",
             OMException.ResultCodes.INVALID_REQUEST);
       }
 
       final S3SecretValue s3SecretValue =
-          new S3SecretValue(principal, awsSecret);
+          new S3SecretValue(accessId, awsSecret);
       omMetadataManager.getS3SecretTable().addCacheEntry(
-          new CacheKey<>(principal),
+          new CacheKey<>(accessId),
           new CacheValue<>(Optional.of(s3SecretValue), transactionLogIndex));
 
       omMetadataManager.getLock().releaseWriteLock(S3_SECRET_LOCK, principal);
       acquiredS3SecretLock = false;
 
+      // Inform MultiTenantManager of user assignment so it could
+      //  initialize some policies in Ranger.
+      // TODO: Is userId from MultiTenantManager still useful?
       userId = ozoneManager.getMultiTenantManager()
-          .createUser(tenantName, tenantUsername);
+          .assignUserToTenant(tenantName, accessId);
       LOG.info("userId = {}", userId);
 
-      // Add to tenantUserTable
-      omMetadataManager.getTenantUserTable().addCacheEntry(
+      // Add to tenantAccessIdTable
+      final OmDBAccessIdInfo omDBAccessIdInfo = new OmDBAccessIdInfo.Builder()
+          .setTenantName(tenantName)
+          .setKerberosPrincipal(principal)
+          .setSharedSecret(s3SecretValue.getAwsSecret())
+          .build();
+      omMetadataManager.getTenantAccessIdTable().addCacheEntry(
+          new CacheKey<>(accessId),
+          new CacheValue<>(Optional.of(omDBAccessIdInfo), 
transactionLogIndex));
+
+      // Add to principalToAccessIdsTable
+      OmDBKerberosPrincipalInfo omDBKerberosPrincipalInfo = omMetadataManager
+          .getPrincipalToAccessIdsTable().getIfExist(principal);
+
+      if (omDBKerberosPrincipalInfo == null) {
+        omDBKerberosPrincipalInfo = new OmDBKerberosPrincipalInfo.Builder()
+            .setAccessIds(new TreeSet<>(Collections.singleton(accessId)))
+            .build();
+      } else {
+        omDBKerberosPrincipalInfo.addAccessId(accessId);
+      }
+      omMetadataManager.getPrincipalToAccessIdsTable().addCacheEntry(
           new CacheKey<>(principal),
-          new CacheValue<>(Optional.of(tenantName), transactionLogIndex));
+          new CacheValue<>(Optional.of(omDBKerberosPrincipalInfo),
+              transactionLogIndex));
+
       // Add to tenantGroupTable
       final String defaultGroupName =
           tenantName + OzoneConsts.DEFAULT_TENANT_USER_GROUP_SUFFIX;
       omMetadataManager.getTenantGroupTable().addCacheEntry(
-          new CacheKey<>(principal),
+          new CacheKey<>(accessId),
           new CacheValue<>(Optional.of(defaultGroupName), 
transactionLogIndex));
+
       // Add to tenantRoleTable
       final String roleName = "role_admin";
       omMetadataManager.getTenantRoleTable().addCacheEntry(
-          new CacheKey<>(principal),
+          new CacheKey<>(accessId),
           new CacheValue<>(Optional.of(roleName), transactionLogIndex));
 
-      omResponse.setCreateTenantUserResponse(
-          CreateTenantUserResponse.newBuilder().setSuccess(true)
+      omResponse.setAssignUserToTenantResponse(
+          AssignUserToTenantResponse.newBuilder().setSuccess(true)
               .setS3Secret(S3Secret.newBuilder()
-                  .setAwsSecret(awsSecret).setKerberosID(principal))
+                  .setAwsSecret(awsSecret).setKerberosID(accessId))
               .build());
-      omClientResponse = new OMTenantUserCreateResponse(omResponse.build(),
-          s3SecretValue, principal, tenantName, defaultGroupName, roleName);
+      omClientResponse = new OMAssignUserToTenantResponse(omResponse.build(),
+          s3SecretValue, principal, defaultGroupName, roleName,
+          accessId, omDBAccessIdInfo, omDBKerberosPrincipalInfo);
     } catch (IOException ex) {
       ozoneManager.getMultiTenantManager().destroyUser(
-          tenantName, tenantUsername);
+          tenantName, accessId);
       exception = ex;
       // Set response success flag to false
-      omResponse.setCreateTenantUserResponse(
-          CreateTenantUserResponse.newBuilder().setSuccess(false).build());
+      omResponse.setAssignUserToTenantResponse(
+          AssignUserToTenantResponse.newBuilder().setSuccess(false).build());
       omClientResponse = new OMTenantCreateResponse(
           createErrorOMResponse(omResponse, ex));
     } finally {
@@ -253,15 +306,17 @@ public class OMTenantUserCreateRequest extends 
OMVolumeRequest {
     // Audit
     auditMap.put(OzoneConsts.TENANT, tenantName);
     auditLog(ozoneManager.getAuditLogger(),
-        buildAuditMessage(OMAction.CREATE_TENANT_USER, auditMap, exception,
+        buildAuditMessage(OMAction.ASSIGN_USER_TO_TENANT, auditMap, exception,
             getOmRequest().getUserInfo()));
 
     if (exception == null) {
-      LOG.info("Created user: {}, in tenant: {}. Principal: {}",
-          tenantUsername, tenantName, principal);
+      LOG.info("Assigned user '{}' to tenant '{}' under accessId '{}'",
+          principal, tenantName, accessId);
       // TODO: omMetrics.incNumTenantUsers()
     } else {
-      LOG.error("Failed to create tenant user {}", tenantName, exception);
+      LOG.error("Failed to assign '{}' to tenant '{}' under accessId '{}': {}",
+          principal, tenantName, accessId, exception.getMessage());
+      // TODO: Check if the exception message is sufficient.
       // TODO: omMetrics.incNumTenantUserCreateFails()
     }
     return omClientResponse;
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantUserCreateResponse.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
similarity index 63%
rename from 
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantUserCreateResponse.java
rename to 
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
index 3832540..56f3c6d 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantUserCreateResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
@@ -20,6 +20,8 @@ package org.apache.hadoop.ozone.om.response.s3.tenant;
 
 import org.apache.hadoop.hdds.utils.db.BatchOperation;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
 import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
 import org.apache.hadoop.ozone.om.response.OMClientResponse;
@@ -27,48 +29,56 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import java.io.IOException;
 
+import static 
org.apache.hadoop.ozone.om.OmMetadataManagerImpl.PRINCIPAL_TO_ACCESS_IDS_TABLE;
 import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.S3_SECRET_TABLE;
+import static 
org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_ACCESS_ID_TABLE;
 import static 
org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_GROUP_TABLE;
 import static 
org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_ROLE_TABLE;
-import static 
org.apache.hadoop.ozone.om.OmMetadataManagerImpl.TENANT_USER_TABLE;
 
 /**
- * Response for OMTenantUserCreate request.
+ * Response for OMAssignUserToTenantRequest.
  */
 @CleanupTableInfo(cleanupTables = {
     S3_SECRET_TABLE,
-    TENANT_USER_TABLE,
+    TENANT_ACCESS_ID_TABLE,
+    PRINCIPAL_TO_ACCESS_IDS_TABLE,
     TENANT_GROUP_TABLE,
     TENANT_ROLE_TABLE
 })
-public class OMTenantUserCreateResponse extends OMClientResponse {
+public class OMAssignUserToTenantResponse extends OMClientResponse {
 
   private S3SecretValue s3SecretValue;
-  private String principal, tenantName, groupName, roleName;
+  private String principal, groupName, roleName, accessId;
+  private OmDBAccessIdInfo omDBAccessIdInfo;
+  private OmDBKerberosPrincipalInfo omDBKerberosPrincipalInfo;
 
-  public OMTenantUserCreateResponse(@Nonnull OMResponse omResponse,
-      @Nullable S3SecretValue s3SecretValue,
-      @Nullable String principal,
-      @Nullable String tenantName,
-      @Nullable String groupName,
-      @Nullable String roleName
+  @SuppressWarnings("checkstyle:parameternumber")
+  public OMAssignUserToTenantResponse(@Nonnull OMResponse omResponse,
+      @Nonnull S3SecretValue s3SecretValue,
+      @Nonnull String principal,
+      @Nonnull String groupName,
+      @Nonnull String roleName,
+      @Nonnull String accessId,
+      @Nonnull OmDBAccessIdInfo omDBAccessIdInfo,
+      @Nonnull OmDBKerberosPrincipalInfo omDBKerberosPrincipalInfo
   ) {
     super(omResponse);
     this.s3SecretValue = s3SecretValue;
     this.principal = principal;
-    this.tenantName = tenantName;
     this.groupName = groupName;
     this.roleName = roleName;
+    this.accessId = accessId;
+    this.omDBAccessIdInfo = omDBAccessIdInfo;
+    this.omDBKerberosPrincipalInfo = omDBKerberosPrincipalInfo;
   }
 
   /**
    * For when the request is not successful.
    * For a successful request, the other constructor should be used.
    */
-  public OMTenantUserCreateResponse(@Nonnull OMResponse omResponse) {
+  public OMAssignUserToTenantResponse(@Nonnull OMResponse omResponse) {
     super(omResponse);
     checkStatusNotOK();
   }
@@ -79,12 +89,15 @@ public class OMTenantUserCreateResponse extends 
OMClientResponse {
 
     if (s3SecretValue != null &&
         getOMResponse().getStatus() == OzoneManagerProtocolProtos.Status.OK) {
+      assert(accessId.equals(s3SecretValue.getKerberosID()));
       omMetadataManager.getS3SecretTable().putWithBatch(batchOperation,
-          s3SecretValue.getKerberosID(), s3SecretValue);
+          accessId, s3SecretValue);
     }
 
-    omMetadataManager.getTenantUserTable().putWithBatch(
-        batchOperation, principal, tenantName);
+    omMetadataManager.getTenantAccessIdTable().putWithBatch(
+        batchOperation, accessId, omDBAccessIdInfo);
+    omMetadataManager.getPrincipalToAccessIdsTable().putWithBatch(
+        batchOperation, principal, omDBKerberosPrincipalInfo);
     omMetadataManager.getTenantGroupTable().putWithBatch(
         batchOperation, principal, groupName);
     omMetadataManager.getTenantRoleTable().putWithBatch(
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/AssignUserToTenantHandler.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/AssignUserToTenantHandler.java
new file mode 100644
index 0000000..1c465d8
--- /dev/null
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/AssignUserToTenantHandler.java
@@ -0,0 +1,126 @@
+/*
+ * 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.s3;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.hdds.cli.GenericCli;
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import picocli.CommandLine;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static 
org.apache.hadoop.ozone.OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER;
+
+/**
+ * ozone s3 user assign.
+ */
[email protected](name = "assign",
+    description = "Assign user to tenant")
+public class AssignUserToTenantHandler extends S3Handler {
+
+  @CommandLine.Spec
+  private CommandLine.Model.CommandSpec spec;
+
+  @CommandLine.Parameters(description = "List of user Kerberos principal(s)")
+  private List<String> principals = new ArrayList<>();
+
+  @CommandLine.Option(names = {"-t", "--tenant"},
+      description = "Tenant name")
+  private String tenantName;
+
+  @CommandLine.Option(names = {"-a", "--access-id", "--accessId"},
+      description = "(Optional) Specify the accessId for user in this tenant. "
+          + "If unspecified, accessId would be in the form of "
+          + "TenantName$Principal.",
+      hidden = true)
+  // This option is intentionally hidden for now. Because accessId isn't
+  //  restricted in any way so far and this could cause some conflict with
+  //  `s3 getsecret` and leak the secret if an admin isn't careful.
+  private String accessId;
+
+  // TODO: support dry-run?
+//  @CommandLine.Option(names = {"--dry-run"},
+//      description = "Dry-run")
+//  private boolean dryRun;
+
+  private boolean isEmptyList(List<String> list) {
+    return list == null || list.size() == 0;
+  }
+
+  private String getDefaultAccessId(String principal) {
+    return tenantName + TENANT_NAME_USER_NAME_DELIMITER + principal;
+  }
+
+  @Override
+  protected void execute(OzoneClient client, OzoneAddress address) {
+    final ObjectStore objStore = client.getObjectStore();
+
+    if (isEmptyList(principals)) {
+      GenericCli.missingSubcommand(spec);
+      return;
+    }
+
+    if (StringUtils.isEmpty(tenantName)) {
+      err().println("Please specify a tenant name with -t.");
+      return;
+    }
+
+    if (StringUtils.isEmpty(accessId)) {
+      accessId = getDefaultAccessId(principals.get(0));
+    } else if (principals.size() > 1) {
+      err().println("Manually specifying accessId is only supported when there 
"
+          + "is one user principal in the command line. Reduce the number of "
+          + "principal to one and try again.");
+      return;
+    }
+
+    for (int i = 0; i < principals.size(); i++) {
+      final String principal = principals.get(i);
+      try {
+        if (i >= 1) {
+          accessId = getDefaultAccessId(principal);
+        }
+        final S3SecretValue resp =
+            objStore.assignUserToTenant(principal, tenantName, accessId);
+        err().println("Assigned '" + principal + "' to '" + tenantName +
+            "' under accessId '" + accessId + "'.");
+        out().println("export AWS_ACCESS_KEY_ID='" +
+            resp.getAwsAccessKey() + "'");
+        out().println("export AWS_SECRET_ACCESS_KEY='" +
+            resp.getAwsSecret() + "'");
+      } catch (IOException e) {
+        err().println("Failed to assign '" + principal + "' to '" +
+            tenantName + "': " + e.getMessage());
+        if (e instanceof OMException) {
+          final OMException omException = (OMException) e;
+          if (omException.getResult().equals(
+              OMException.ResultCodes.TENANT_NOT_FOUND)) {
+            // If tenant does not exist, don't bother continuing the loop
+            break;
+          }
+        }
+      }
+    }
+  }
+}
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCreateHandler.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCreateHandler.java
index 071e05c..672832e 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCreateHandler.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCreateHandler.java
@@ -45,7 +45,7 @@ public class TenantCreateHandler extends S3Handler {
       for (String tenantName : tenants) {
         try {
           client.getObjectStore().createTenant(tenantName);
-          out().println("Successfully created tenant " + tenantName);
+          out().println("Created tenant " + tenantName);
         } catch (IOException e) {
           out().println("Failed to create tenant " + tenantName + ": " +
               e.getMessage());
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCommands.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCommands.java
index 6b82919..8db2484 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCommands.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCommands.java
@@ -35,7 +35,7 @@ import java.util.concurrent.Callable;
 @CommandLine.Command(name = "user",
     description = "Tenant user management",
     subcommands = {
-        TenantUserCreateHandler.class,
+        AssignUserToTenantHandler.class,
         TenantUserModifyHandler.class,
         TenantUserDeleteHandler.class
     },
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCreateHandler.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCreateHandler.java
deleted file mode 100644
index a26c5d0..0000000
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCreateHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.s3;
-
-import org.apache.hadoop.hdds.cli.GenericCli;
-import org.apache.hadoop.ozone.client.ObjectStore;
-import org.apache.hadoop.ozone.client.OzoneClient;
-import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
-import org.apache.hadoop.ozone.shell.OzoneAddress;
-import picocli.CommandLine;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * ozone s3 user create.
- */
[email protected](name = "create",
-    description = "Create one or more tenant users")
-public class TenantUserCreateHandler extends S3Handler {
-
-  @CommandLine.Spec
-  private CommandLine.Model.CommandSpec spec;
-
-  @CommandLine.Parameters(description = "List of tenant user short names")
-  private List<String> usernames = new ArrayList<>();
-
-  @CommandLine.Option(names = "-t",
-      description = "Tenant name")
-  private String tenantName;
-
-  @Override
-  protected void execute(OzoneClient client, OzoneAddress address) {
-    final ObjectStore objStore = client.getObjectStore();
-    if (tenantName == null || tenantName.length() == 0) {
-      tenantName = objStore.getS3VolumeName();
-    }
-    if (usernames.size() > 0) {
-      for (String username : usernames) {
-        try {
-          S3SecretValue res = objStore.createTenantUser(username, tenantName);
-          out().println("Successfully created user " + username + ":");
-          out().println("export AWS_ACCESS_KEY_ID=" + res.getAwsAccessKey());
-          out().println("export AWS_SECRET_ACCESS_KEY=" + res.getAwsSecret());
-        } catch (IOException e) {
-          out().println("Failed to create user " + username + ": " +
-              e.getMessage());
-        }
-      }
-    } else {
-      GenericCli.missingSubcommand(spec);
-    }
-  }
-}

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

Reply via email to