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 8516765  HDDS-5750. [Multi-Tenant] GetS3Secret should retrieve secret 
from new tables as well (#2649)
8516765 is described below

commit 85167656ffa4661df252863cbdd49123de7575a0
Author: Siyao Meng <[email protected]>
AuthorDate: Wed Sep 22 14:46:42 2021 -0700

    HDDS-5750. [Multi-Tenant] GetS3Secret should retrieve secret from new 
tables as well (#2649)
---
 .../om/request/s3/security/S3GetSecretRequest.java |  76 ++--
 .../s3/tenant/OMAssignUserToTenantRequest.java     |  13 +-
 .../response/s3/security/S3GetSecretResponse.java  |   6 +
 .../s3/tenant/OMAssignUserToTenantResponse.java    |   7 +
 .../response/s3/tenant/OMTenantCreateResponse.java |   6 +
 .../s3/security/TestS3GetSecretRequest.java        | 418 +++++++++++++++++++++
 .../hadoop/ozone/shell/s3/GetS3SecretHandler.java  |  10 +-
 7 files changed, 495 insertions(+), 41 deletions(-)

diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3GetSecretRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3GetSecretRequest.java
index 44c69e9..781c95c 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3GetSecretRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3GetSecretRequest.java
@@ -24,6 +24,7 @@ import java.util.Map;
 
 import com.google.common.base.Optional;
 import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
 import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -83,6 +84,8 @@ public class S3GetSecretRequest extends OMClientRequest {
           OMException.ResultCodes.USER_MISMATCH);
     }
 
+    // Generate secret. Used only when doesn't the kerberosID entry doesn't
+    //  exist in DB, discarded otherwise.
     String s3Secret = DigestUtils.sha256Hex(OmUtils.getSHADigest());
 
     UpdateGetS3SecretRequest updateGetS3SecretRequest =
@@ -126,38 +129,48 @@ public class S3GetSecretRequest extends OMClientRequest {
     String kerberosID = updateGetS3SecretRequest.getKerberosID();
     try {
       String awsSecret = updateGetS3SecretRequest.getAwsSecret();
-      acquiredLock =
-         omMetadataManager.getLock().acquireWriteLock(S3_SECRET_LOCK,
-             kerberosID);
-
-      S3SecretValue s3SecretValue =
-          omMetadataManager.getS3SecretTable().get(kerberosID);
-
-      // If s3Secret for user is not in S3Secret table, add the Secret to 
cache.
-      if (s3SecretValue == null) {
-        omMetadataManager.getS3SecretTable().addCacheEntry(
-            new CacheKey<>(kerberosID),
-            new CacheValue<>(Optional.of(new S3SecretValue(kerberosID,
-                awsSecret)), transactionLogIndex));
+      // Note: We use the same S3_SECRET_LOCK for TenantAccessIdTable.
+      acquiredLock = omMetadataManager.getLock()
+          .acquireWriteLock(S3_SECRET_LOCK, kerberosID);
+
+      // Check multi-tenant table first: tenantAccessIdTable
+      final S3SecretValue assignS3SecretValue;
+      final OmDBAccessIdInfo omDBAccessIdInfo =
+          omMetadataManager.getTenantAccessIdTable().get(kerberosID);
+      if (omDBAccessIdInfo == null) {
+        // Not found in TenantAccessIdTable. Fallback to S3SecretTable.
+        final S3SecretValue s3SecretValue =
+            omMetadataManager.getS3SecretTable().get(kerberosID);
+
+        if (s3SecretValue == null) {
+          // Still not found in S3SecretTable. Will add new entry in this case.
+          assignS3SecretValue = new S3SecretValue(kerberosID, awsSecret);
+          // Add cache entry first.
+          omMetadataManager.getS3SecretTable().addCacheEntry(
+              new CacheKey<>(kerberosID),
+              new CacheValue<>(
+                  Optional.of(assignS3SecretValue), transactionLogIndex));
+        } else {
+          // Found in S3SecretTable.
+          awsSecret = s3SecretValue.getAwsSecret();
+          assignS3SecretValue = null;
+        }
       } else {
-        // If it already exists, use the existing one.
-        awsSecret = s3SecretValue.getAwsSecret();
+        // Found in TenantAccessIdTable.
+        awsSecret = omDBAccessIdInfo.getSharedSecret();
+        assignS3SecretValue = null;
       }
 
-      GetS3SecretResponse.Builder getS3SecretResponse = GetS3SecretResponse
-          .newBuilder().setS3Secret(S3Secret.newBuilder()
-          .setAwsSecret(awsSecret).setKerberosID(kerberosID));
-
-      if (s3SecretValue == null) {
-        omClientResponse =
-            new S3GetSecretResponse(new S3SecretValue(kerberosID, awsSecret),
-            omResponse.setGetS3SecretResponse(getS3SecretResponse).build());
-      } else {
-        // As when it already exists, we don't need to add to DB again. So
-        // set the value to null.
-        omClientResponse = new S3GetSecretResponse(null,
-            omResponse.setGetS3SecretResponse(getS3SecretResponse).build());
-      }
+      // Compose response
+      final GetS3SecretResponse.Builder getS3SecretResponse =
+          GetS3SecretResponse.newBuilder().setS3Secret(
+              S3Secret.newBuilder()
+                  .setAwsSecret(awsSecret)
+                  .setKerberosID(kerberosID));
+      // If entry exists, assignS3SecretValue will be null,
+      // so we won't overwrite the entry.
+      omClientResponse = new S3GetSecretResponse(assignS3SecretValue,
+          omResponse.setGetS3SecretResponse(getS3SecretResponse).build());
 
     } catch (IOException ex) {
       exception = ex;
@@ -182,10 +195,9 @@ public class S3GetSecretRequest extends OMClientRequest {
         exception, getOmRequest().getUserInfo()));
 
     if (exception == null) {
-      LOG.debug("Secret for accessKey:{} is generated Successfully",
-          kerberosID);
+      LOG.debug("Successfully generated secret for accessKey '{}'", 
kerberosID);
     } else {
-      LOG.error("Secret for accessKey:{} is generation failed", kerberosID,
+      LOG.error("Failed to generate secret for accessKey '{}': {}", kerberosID,
           exception);
     }
     return omClientResponse;
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 e49b4b6..31a9b8f 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
@@ -80,7 +80,8 @@ import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_L
       - Value: <GENERATED_SECRET>
     - Release S3_SECRET_LOCK write lock
     - New entry in tenantAccessIdTable:
-      - Key: New accessId for the user in this tenant. e.g. finance$bob ?
+      - Key: New accessId for the user in this tenant.
+             Example of accessId: [email protected]
       - Value: OmDBAccessIdInfo. Has tenantId, kerberosPrincipal, sharedSecret.
     - New entry or update existing entry in principalToAccessIdsTable:
       - Key: Kerberos principal of the user.
@@ -88,7 +89,7 @@ import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_L
     - tenantGroupTable: Add this new user to the default tenant group.
       - Key: finance$bob
       - Value: finance-users
-    - tenantRoleTable: TBD. No-op for prototype.
+    - tenantRoleTable: TBD. No-Op for now.
     - Release VOLUME_LOCK write lock
  */
 
@@ -128,9 +129,10 @@ public class OMAssignUserToTenantRequest extends 
OMVolumeRequest {
     }
 
     // Won't check tenant existence in preExecute.
-    // Won't check Kerberos principal existence at all. TODO: Confirm this.
+    // Won't check Kerberos principal existence.
 
-    // Generate random S3 secret
+    // Generate secret. Used only when doesn't the kerberosID entry doesn't
+    //  exist in DB, discarded otherwise.
     final String s3Secret = DigestUtils.sha256Hex(OmUtils.getSHADigest());
 
     final UpdateGetS3SecretRequest updateGetS3SecretRequest =
@@ -179,7 +181,7 @@ public class OMAssignUserToTenantRequest extends 
OMVolumeRequest {
     assert(accessId.equals(request.getAccessId()));
     final String volumeName = tenantName;  // TODO: Configurable
     IOException exception = null;
-    String userId = null;
+    String userId;
 
     try {
       // Check ACL: requires ozone admin or tenant admin permission
@@ -220,6 +222,7 @@ public class OMAssignUserToTenantRequest extends 
OMVolumeRequest {
 
       final S3SecretValue s3SecretValue =
           new S3SecretValue(accessId, awsSecret);
+      // Add S3SecretTable cache entry
       omMetadataManager.getS3SecretTable().addCacheEntry(
           new CacheKey<>(accessId),
           new CacheValue<>(Optional.of(s3SecretValue), transactionLogIndex));
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/S3GetSecretResponse.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/S3GetSecretResponse.java
index 1cd0ce5..29f64ca 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/S3GetSecretResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/S3GetSecretResponse.java
@@ -18,6 +18,7 @@
 
 package org.apache.hadoop.ozone.om.response.s3.security;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
 import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
@@ -56,4 +57,9 @@ public class S3GetSecretResponse extends OMClientResponse {
           s3SecretValue.getKerberosID(), s3SecretValue);
     }
   }
+
+  @VisibleForTesting
+  public S3SecretValue getS3SecretValue() {
+    return s3SecretValue;
+  }
 }
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
index 56f3c6d..f8e1faf 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMAssignUserToTenantResponse.java
@@ -18,6 +18,7 @@
  */
 package org.apache.hadoop.ozone.om.response.s3.tenant;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.hdds.utils.db.BatchOperation;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
@@ -90,6 +91,7 @@ public class OMAssignUserToTenantResponse extends 
OMClientResponse {
     if (s3SecretValue != null &&
         getOMResponse().getStatus() == OzoneManagerProtocolProtos.Status.OK) {
       assert(accessId.equals(s3SecretValue.getKerberosID()));
+      // Add S3SecretTable entry
       omMetadataManager.getS3SecretTable().putWithBatch(batchOperation,
           accessId, s3SecretValue);
     }
@@ -103,4 +105,9 @@ public class OMAssignUserToTenantResponse extends 
OMClientResponse {
     omMetadataManager.getTenantRoleTable().putWithBatch(
         batchOperation, principal, roleName);
   }
+
+  @VisibleForTesting
+  public OmDBAccessIdInfo getOmDBAccessIdInfo() {
+    return omDBAccessIdInfo;
+  }
 }
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
index 9e5b481..99905d6 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
@@ -18,6 +18,7 @@
  */
 package org.apache.hadoop.ozone.om.response.s3.tenant;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.hdds.utils.db.BatchOperation;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.om.helpers.OmDBTenantInfo;
@@ -103,4 +104,9 @@ public class OMTenantCreateResponse extends 
OMClientResponse {
     omMetadataManager.getUserTable().putWithBatch(batchOperation, dbUserKey,
         userVolumeInfo);
   }
+
+  @VisibleForTesting
+  public OmDBTenantInfo getOmDBTenantInfo() {
+    return omTenantInfo;
+  }
 }
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
new file mode 100644
index 0000000..d8f67e9
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
@@ -0,0 +1,418 @@
+/**
+ * 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.request.s3.security;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.ipc.RPC;
+import org.apache.hadoop.ipc.Server;
+import org.apache.hadoop.ipc.Server.Call;
+import org.apache.hadoop.ozone.audit.AuditLogger;
+import org.apache.hadoop.ozone.audit.AuditMessage;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OMMetrics;
+import org.apache.hadoop.ozone.om.OMMultiTenantManager;
+import org.apache.hadoop.ozone.om.OmMetadataManagerImpl;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.multitenant.Tenant;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import 
org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
+import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.security.S3GetSecretResponse;
+import 
org.apache.hadoop.ozone.om.response.s3.tenant.OMAssignUserToTenantResponse;
+import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Secret;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import static 
org.apache.hadoop.ozone.OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER;
+import static 
org.apache.hadoop.security.authentication.util.KerberosName.DEFAULT_MECHANISM;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.framework;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test S3GetSecretRequest.
+ */
+public class TestS3GetSecretRequest {
+
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
+  private OzoneManager ozoneManager;
+  private OMMetrics omMetrics;
+  private OMMetadataManager omMetadataManager;
+  private AuditLogger auditLogger;
+  // Set ozoneManagerDoubleBuffer to do nothing.
+  private final OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper =
+      ((response, transactionIndex) -> null);
+
+  // Multi-tenant related vars
+  private static final String USER_ALICE = "[email protected]";
+  private static final String TENANT_NAME = "finance";
+  private static final String USER_BOB = "[email protected]";
+  private static final String ACCESS_ID_BOB =
+      TENANT_NAME + TENANT_NAME_USER_NAME_DELIMITER + USER_BOB;
+
+  private UserGroupInformation ugiAlice;
+
+  private OMMultiTenantManager omMultiTenantManager;
+  private Tenant tenant;
+
+  @Before
+  public void setUp() throws Exception {
+    KerberosName.setRuleMechanism(DEFAULT_MECHANISM);
+    KerberosName.setRules(
+        "RULE:[2:$1@$0](.*@EXAMPLE.COM)s/@.*//\n" +
+        "RULE:[1:$1@$0](.*@EXAMPLE.COM)s/@.*//\n" +
+        "DEFAULT");
+    ugiAlice = UserGroupInformation.createRemoteUser(USER_ALICE);
+    Assert.assertEquals("alice", ugiAlice.getShortUserName());
+
+    ozoneManager = mock(OzoneManager.class);
+
+    Call call = spy(new Call(1, 1, null, null,
+        RPC.RpcKind.RPC_BUILTIN, new byte[] {1, 2, 3}));
+    // Run as alice, so that Server.getRemoteUser() won't return null.
+    when(call.getRemoteUser()).thenReturn(ugiAlice);
+    Server.getCurCall().set(call);
+
+    omMetrics = OMMetrics.create();
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(OMConfigKeys.OZONE_OM_DB_DIRS,
+        folder.newFolder().getAbsolutePath());
+    // No need to conf.set(OzoneConfigKeys.OZONE_ADMINISTRATORS, ...) here
+    //  as we did the trick earlier with mockito.
+    omMetadataManager = new OmMetadataManagerImpl(conf);
+    when(ozoneManager.getMetrics()).thenReturn(omMetrics);
+    when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager);
+    when(ozoneManager.isRatisEnabled()).thenReturn(true);
+    auditLogger = mock(AuditLogger.class);
+    when(ozoneManager.getAuditLogger()).thenReturn(auditLogger);
+    doNothing().when(auditLogger).logWrite(any(AuditMessage.class));
+
+    // Multi-tenant related initializations
+    omMultiTenantManager = mock(OMMultiTenantManager.class);
+    tenant = mock(Tenant.class);
+    when(omMultiTenantManager.getTenantInfo(TENANT_NAME)).thenReturn(tenant);
+    
when(ozoneManager.getMultiTenantManager()).thenReturn(omMultiTenantManager);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    omMetrics.unRegister();
+    framework().clearInlineMocks();
+  }
+
+  private OMRequest createTenantRequest(String tenantNameStr) {
+
+    return OMRequest.newBuilder()
+        .setClientId(UUID.randomUUID().toString())
+        .setCmdType(Type.CreateTenant)
+        .setCreateTenantRequest(
+            CreateTenantRequest.newBuilder()
+                .setTenantName(tenantNameStr)
+                .build()
+        ).build();
+  }
+
+  private OMRequest assignUserToTenantRequest(
+      String tenantNameStr, String userPrincipalStr, String accessIdStr) {
+
+    return OMRequest.newBuilder()
+        .setClientId(UUID.randomUUID().toString())
+        .setCmdType(Type.AssignUserToTenant)
+        .setAssignUserToTenantRequest(
+            AssignUserToTenantRequest.newBuilder()
+                .setTenantName(tenantNameStr)
+                .setTenantUsername(userPrincipalStr)
+                .setAccessId(accessIdStr)
+                .build()
+        ).build();
+  }
+
+  private OMRequest s3GetSecretRequest(String userPrincipalStr) {
+
+    return OMRequest.newBuilder()
+        .setClientId(UUID.randomUUID().toString())
+        .setCmdType(Type.GetS3Secret)
+        .setGetS3SecretRequest(
+            GetS3SecretRequest.newBuilder()
+                .setKerberosID(userPrincipalStr)
+                .build()
+        ).build();
+  }
+
+  @Test
+  public void testGetSecretOfAnotherUserAsAdmin() throws IOException {
+
+    // This effectively makes alice an admin.
+    when(ozoneManager.isAdmin(ugiAlice)).thenReturn(true);
+
+    // 1. Get secret of "[email protected]" (as an admin).
+    long txLogIndex = 1;
+
+    // Run preExecute
+    S3GetSecretRequest s3GetSecretRequest =
+        new S3GetSecretRequest(
+            new S3GetSecretRequest(
+                s3GetSecretRequest(ACCESS_ID_BOB)
+            ).preExecute(ozoneManager)
+        );
+
+    // Run validateAndUpdateCache
+    OMClientResponse omClientResponse =
+        s3GetSecretRequest.validateAndUpdateCache(ozoneManager,
+            txLogIndex, ozoneManagerDoubleBufferHelper);
+
+    // Check response type and cast
+    Assert.assertTrue(omClientResponse instanceof S3GetSecretResponse);
+    final S3GetSecretResponse s3GetSecretResponse =
+        (S3GetSecretResponse) omClientResponse;
+
+    // Check response
+    final S3SecretValue s3SecretValue = s3GetSecretResponse.getS3SecretValue();
+    Assert.assertEquals(ACCESS_ID_BOB, s3SecretValue.getKerberosID());
+    final String awsSecret = s3SecretValue.getAwsSecret();
+    Assert.assertNotNull(awsSecret);
+
+    final GetS3SecretResponse getS3SecretResponse =
+        s3GetSecretResponse.getOMResponse().getGetS3SecretResponse();
+    // The secret inside should be the same.
+    final S3Secret s3Secret = getS3SecretResponse.getS3Secret();
+    Assert.assertEquals(ACCESS_ID_BOB, s3Secret.getKerberosID());
+    Assert.assertEquals(awsSecret, s3Secret.getAwsSecret());
+  }
+
+  @Test
+  public void testGetOwnSecretAsNonAdmin() throws IOException {
+
+    // This effectively makes alice a regular user.
+    when(ozoneManager.isAdmin(ugiAlice)).thenReturn(false);
+
+    // 1. Get secret of "alice" (as herself).
+    long txLogIndex = 1;
+
+    // Run preExecute
+    S3GetSecretRequest s3GetSecretRequest1 =
+        new S3GetSecretRequest(
+            new S3GetSecretRequest(
+                s3GetSecretRequest(USER_ALICE)
+            ).preExecute(ozoneManager)
+        );
+
+    // Run validateAndUpdateCache
+    OMClientResponse omClientResponse =
+        s3GetSecretRequest1.validateAndUpdateCache(ozoneManager,
+            txLogIndex, ozoneManagerDoubleBufferHelper);
+
+    // Check response type and cast
+    Assert.assertTrue(omClientResponse instanceof S3GetSecretResponse);
+    final S3GetSecretResponse s3GetSecretResponse =
+        (S3GetSecretResponse) omClientResponse;
+
+    // Check response
+    final S3SecretValue s3SecretValue = s3GetSecretResponse.getS3SecretValue();
+    Assert.assertEquals(USER_ALICE, s3SecretValue.getKerberosID());
+    final String awsSecret1 = s3SecretValue.getAwsSecret();
+    Assert.assertNotNull(awsSecret1);
+
+    final GetS3SecretResponse getS3SecretResponse =
+        s3GetSecretResponse.getOMResponse().getGetS3SecretResponse();
+    // The secret inside should be the same.
+    final S3Secret s3Secret1 = getS3SecretResponse.getS3Secret();
+    Assert.assertEquals(USER_ALICE, s3Secret1.getKerberosID());
+    Assert.assertEquals(awsSecret1, s3Secret1.getAwsSecret());
+
+
+    // 2. Get secret of "alice" (as herself) again.
+    ++txLogIndex;
+
+    // Run preExecute
+    S3GetSecretRequest s3GetSecretRequest2 =
+        new S3GetSecretRequest(
+            new S3GetSecretRequest(
+                s3GetSecretRequest(USER_ALICE)
+            ).preExecute(ozoneManager)
+        );
+
+    // Run validateAndUpdateCache
+    OMClientResponse omClientResponse2 =
+        s3GetSecretRequest2.validateAndUpdateCache(ozoneManager,
+            txLogIndex, ozoneManagerDoubleBufferHelper);
+
+    // Check response type and cast
+    Assert.assertTrue(omClientResponse2 instanceof S3GetSecretResponse);
+    final S3GetSecretResponse s3GetSecretResponse2 =
+        (S3GetSecretResponse) omClientResponse2;
+
+    // Check response
+    Assert.assertNull(s3GetSecretResponse2.getS3SecretValue());
+
+    final GetS3SecretResponse getS3SecretResponse2 =
+        s3GetSecretResponse2.getOMResponse().getGetS3SecretResponse();
+    // The secret inside should be the same.
+    final S3Secret s3Secret2 = getS3SecretResponse2.getS3Secret();
+    Assert.assertEquals(USER_ALICE, s3Secret2.getKerberosID());
+
+    // Should get the same secret as the first request's.
+    Assert.assertEquals(awsSecret1, s3Secret2.getAwsSecret());
+  }
+
+  @Test
+  public void testGetSecretOfAnotherUserAsNonAdmin() throws IOException {
+
+    // This effectively makes alice a regular user.
+    when(ozoneManager.isAdmin(ugiAlice)).thenReturn(false);
+
+    // Get secret of "[email protected]" (as another regular user).
+    // Run preExecute, expect USER_MISMATCH
+    try {
+      new S3GetSecretRequest(
+          s3GetSecretRequest(ACCESS_ID_BOB)
+      ).preExecute(ozoneManager);
+    } catch (OMException omEx) {
+      Assert.assertEquals(ResultCodes.USER_MISMATCH, omEx.getResult());
+      return;
+    }
+
+    Assert.fail("Should have thrown OMException because alice should not have "
+        + "the permission to get bob's secret in this test case!");
+  }
+
+  @Test
+  public void testGetSecretWithTenant() throws IOException {
+
+    // This effectively makes alice an admin.
+    when(ozoneManager.isAdmin(ugiAlice)).thenReturn(true);
+
+    // 1. CreateTenantRequest: Create tenant "finance".
+    long txLogIndex = 1;
+    // Run preExecute
+    OMTenantCreateRequest omTenantCreateRequest =
+        new OMTenantCreateRequest(
+            new OMTenantCreateRequest(
+                createTenantRequest(TENANT_NAME)
+            ).preExecute(ozoneManager)
+        );
+    // Run validateAndUpdateCache
+    OMClientResponse omClientResponse =
+        omTenantCreateRequest.validateAndUpdateCache(ozoneManager,
+            txLogIndex, ozoneManagerDoubleBufferHelper);
+    // Check response type and cast
+    Assert.assertTrue(omClientResponse instanceof OMTenantCreateResponse);
+    final OMTenantCreateResponse omTenantCreateResponse =
+        (OMTenantCreateResponse) omClientResponse;
+    // Check response
+    Assert.assertTrue(omTenantCreateResponse.getOMResponse().getSuccess());
+    Assert.assertEquals(TENANT_NAME,
+        omTenantCreateResponse.getOmDBTenantInfo().getTenantName());
+
+
+    // 2. AssignUserToTenantRequest: Assign "[email protected]" to "finance".
+    ++txLogIndex;
+    // Run preExecute
+    OMAssignUserToTenantRequest omAssignUserToTenantRequest =
+        new OMAssignUserToTenantRequest(
+            new OMAssignUserToTenantRequest(
+                assignUserToTenantRequest(TENANT_NAME, USER_BOB, ACCESS_ID_BOB)
+            ).preExecute(ozoneManager)
+        );
+
+    // Run validateAndUpdateCache
+    omClientResponse =
+        omAssignUserToTenantRequest.validateAndUpdateCache(ozoneManager,
+            txLogIndex, ozoneManagerDoubleBufferHelper);
+
+    // Check response type and cast
+    Assert.assertTrue(omClientResponse instanceof 
OMAssignUserToTenantResponse);
+    final OMAssignUserToTenantResponse omAssignUserToTenantResponse =
+        (OMAssignUserToTenantResponse) omClientResponse;
+
+    // Check response
+    Assert.assertTrue(omAssignUserToTenantResponse.getOMResponse()
+        .getSuccess());
+    Assert.assertTrue(omAssignUserToTenantResponse.getOMResponse()
+        .getAssignUserToTenantResponse().getSuccess());
+    final OmDBAccessIdInfo omDBAccessIdInfo =
+        omAssignUserToTenantResponse.getOmDBAccessIdInfo();
+    Assert.assertNotNull(omDBAccessIdInfo);
+
+
+    // 3. S3GetSecretRequest: Get secret of "[email protected]" (as an admin).
+    ++txLogIndex;
+
+    // Run preExecute
+    S3GetSecretRequest s3GetSecretRequest =
+        new S3GetSecretRequest(
+            new S3GetSecretRequest(
+                s3GetSecretRequest(ACCESS_ID_BOB)
+            ).preExecute(ozoneManager)
+        );
+
+    // Run validateAndUpdateCache
+    omClientResponse =
+        s3GetSecretRequest.validateAndUpdateCache(ozoneManager,
+            txLogIndex, ozoneManagerDoubleBufferHelper);
+
+    // Check response type and cast
+    Assert.assertTrue(omClientResponse instanceof S3GetSecretResponse);
+    final S3GetSecretResponse s3GetSecretResponse =
+        (S3GetSecretResponse) omClientResponse;
+
+    // Check response
+    Assert.assertTrue(s3GetSecretResponse.getOMResponse().getSuccess());
+    /*
+       getS3SecretValue() should be null in this case because
+       the entry is already inserted to DB in the previous request.
+       The entry will get overwritten if it isn't null.
+       See {@link S3GetSecretResponse#addToDBBatch}.
+     */
+    Assert.assertNull(s3GetSecretResponse.getS3SecretValue());
+    // The secret retrieved should be the same as previous response's.
+    final GetS3SecretResponse getS3SecretResponse =
+        s3GetSecretResponse.getOMResponse().getGetS3SecretResponse();
+    final S3Secret s3Secret = getS3SecretResponse.getS3Secret();
+    Assert.assertEquals(ACCESS_ID_BOB, s3Secret.getKerberosID());
+    Assert.assertEquals(
+        omDBAccessIdInfo.getSharedSecret(), s3Secret.getAwsSecret());
+  }
+}
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/GetS3SecretHandler.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/GetS3SecretHandler.java
index df58eea..aa4d893 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/GetS3SecretHandler.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/GetS3SecretHandler.java
@@ -35,8 +35,8 @@ import picocli.CommandLine.Option;
 public class GetS3SecretHandler extends S3Handler {
 
   @Option(names = "-u",
-      description = "Specify the user name to perform the operation on "
-          + "(admins only)'")
+      description = "Specify the user to perform the operation on "
+          + "(Admins only)'")
   private String username;
 
   @Option(names = "-e",
@@ -58,8 +58,10 @@ public class GetS3SecretHandler extends S3Handler {
 
     final S3SecretValue secret = client.getObjectStore().getS3Secret(username);
     if (export) {
-      out().println("export AWS_ACCESS_KEY_ID=" + secret.getAwsAccessKey());
-      out().println("export AWS_SECRET_ACCESS_KEY=" + secret.getAwsSecret());
+      out().println("export AWS_ACCESS_KEY_ID='" +
+          secret.getAwsAccessKey() + "'");
+      out().println("export AWS_SECRET_ACCESS_KEY='" +
+          secret.getAwsSecret() + "'");
     } else {
       out().println(secret);
     }

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

Reply via email to