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

chungen0126 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new a57788ea4cd HDDS-12250. Speed up TestSecureOzoneCluster (#10834)
a57788ea4cd is described below

commit a57788ea4cda64c2130ce6087a96835f201dd0ca
Author: Chia-Yi Chiu <[email protected]>
AuthorDate: Thu Jul 23 11:19:39 2026 +0900

    HDDS-12250. Speed up TestSecureOzoneCluster (#10834)
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../apache/hadoop/ozone/TestDelegationToken.java   |   66 +-
 .../hadoop/ozone/TestSecureOzoneCluster.java       | 1009 ++++++++------------
 2 files changed, 486 insertions(+), 589 deletions(-)

diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
index 1092517abce..444b6c313fa 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestDelegationToken.java
@@ -35,12 +35,14 @@
 import static org.apache.hadoop.net.ServerSocketUtil.getPort;
 import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_ADMINISTRATORS;
 import static 
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY;
+import static 
org.apache.hadoop.ozone.om.OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_KEYTAB_FILE_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_AUTH_METHOD;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER;
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.VOLUME_NOT_FOUND;
 import static 
org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -262,7 +264,9 @@ private void initSCM() throws IOException {
    * 3. Client can authenticate using token.
    * 4. Delegation token renewal without Kerberos auth fails.
    * 5. Test success of token cancellation.
-   * 5. Test failure of token cancellation.
+   * 6. Test failure of token cancellation.
+   * 7. Test delegation token renewal failures (expired maxDate, non-matching
+   *    renewer, tampered token).
    */
   @ParameterizedTest
   @MethodSource("options")
@@ -277,6 +281,10 @@ public void testDelegationToken(boolean useIp) throws 
Exception {
     GenericTestUtils.setLogLevel(Server.class, INFO);
     SecurityUtil.setTokenServiceUseIp(useIp);
 
+    // Generous token lifetime so the renewal-failure cases below do not race
+    // token expiry.
+    conf.setLong(DELEGATION_TOKEN_MAX_LIFETIME_KEY, 60 * 1000L);
+
     // Setup secure OM for start
     setupOm(conf);
 
@@ -398,12 +406,68 @@ public void testDelegationToken(boolean useIp) throws 
Exception {
       assertEquals(TOKEN_ERROR_OTHER, ex.getResult());
       assertThat(ex.getMessage()).contains("Cancel delegation token failed");
       assertThat(logs.getOutput()).contains("Auth failed for");
+
+      // Case 7: Delegation token renewal failures.
+      assertDelegationTokenRenewalFailures(ugi, omLogs);
     } finally {
       om.stop();
       om.join();
     }
   }
 
+  /**
+   * Exercises the delegation token renewal failure paths against the running
+   * OM. Reconnects via Kerberos and seeds a fresh token, then verifies renewal
+   * fails for an expired maxDate, a non-matching renewer, and a tampered 
token.
+   */
+  private void assertDelegationTokenRenewalFailures(
+      UserGroupInformation ugi, LogCapturer omLogs) throws Exception {
+    omClient.close();
+    UserGroupInformation.setLoginUser(ugi);
+    omClient = new OzoneManagerProtocolClientSideTranslatorPB(
+        OmTransportFactory.create(conf, ugi, null),
+        RandomStringUtils.secure().nextAscii(5));
+    Token<OzoneTokenIdentifier> seedToken =
+        omClient.getDelegationToken(new Text("om"));
+
+    // 1. When token maxExpiryTime exceeds (maxDate in the past).
+    OzoneTokenIdentifier expiredId = OzoneTokenIdentifier.readProtoBuf(
+        seedToken.getIdentifier());
+    expiredId.setMaxDate(System.currentTimeMillis() - 1000);
+    Token<OzoneTokenIdentifier> expiredToken = new Token<>(
+        expiredId.getBytes(), seedToken.getPassword(), seedToken.getKind(),
+        seedToken.getService());
+    OMException ex = assertThrows(OMException.class,
+        () -> omClient.renewDelegationToken(expiredToken));
+    assertEquals(TOKEN_EXPIRED, ex.getResult());
+    omLogs.clearOutput();
+
+    // 2. When renewer doesn't match (implicitly covers when renewer is
+    // null or empty).
+    Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
+        new Text("randomService"));
+    assertNotNull(token2);
+    ex = assertThrows(OMException.class,
+        () -> omClient.renewDelegationToken(token2));
+    assertThat(ex).hasMessageContaining("Delegation token renewal failed");
+    assertThat(omLogs.getOutput()).contains(" with non-matching renewer 
randomService");
+    omLogs.clearOutput();
+
+    // 3. Tampered token that can't be found in cache.
+    OzoneTokenIdentifier tokenId = OzoneTokenIdentifier.readProtoBuf(
+        seedToken.getIdentifier());
+    tokenId.setRenewer(new Text("om"));
+    tokenId.setMaxDate(System.currentTimeMillis() * 2);
+    Token<OzoneTokenIdentifier> tamperedToken = new Token<>(
+        tokenId.getBytes(), token2.getPassword(), token2.getKind(),
+        token2.getService());
+    ex = assertThrows(OMException.class,
+        () -> omClient.renewDelegationToken(tamperedToken));
+    assertThat(ex).hasMessageContaining("Delegation token renewal failed");
+    assertThat(omLogs.getOutput()).contains("can't be found in cache");
+    omLogs.clearOutput();
+  }
+
   private void generateKeyPair() throws Exception {
     SecurityConfig securityConfig = new SecurityConfig(conf);
     HDDSKeyGenerator keyGenerator = new HDDSKeyGenerator(securityConfig);
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
index 386ba7c8240..bb9fb9f6180 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
@@ -44,7 +44,6 @@
 import static 
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY;
 import static 
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY;
 import static org.apache.hadoop.ozone.OzoneConsts.SCM_SUB_CA;
-import static 
org.apache.hadoop.ozone.om.OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY;
 import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY;
@@ -52,7 +51,6 @@
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_S3_GPRC_SERVER_ENABLED;
 import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_TRANSPORT_CLASS;
-import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.USER_MISMATCH;
 import static 
org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
 import static org.apache.ozone.test.GenericTestUtils.PortAllocator.getFreePort;
@@ -102,12 +100,10 @@
 import 
org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
 import 
org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
 import org.apache.hadoop.hdds.scm.HddsTestUtils;
-import org.apache.hadoop.hdds.scm.ScmConfig;
 import org.apache.hadoop.hdds.scm.ScmInfo;
 import org.apache.hadoop.hdds.scm.client.ScmTopologyClient;
 import org.apache.hadoop.hdds.scm.protocol.ScmBlockLocationProtocol;
 import org.apache.hadoop.hdds.scm.protocol.StorageContainerLocationProtocol;
-import org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig;
 import org.apache.hadoop.hdds.scm.server.SCMStorageConfig;
 import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
 import org.apache.hadoop.hdds.security.SecurityConfig;
@@ -128,7 +124,6 @@
 import org.apache.hadoop.hdds.utils.HAUtils;
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.ipc_.Client;
-import org.apache.hadoop.ipc_.Server;
 import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
 import org.apache.hadoop.minikdc.MiniKdc;
 import org.apache.hadoop.ozone.client.OzoneClient;
@@ -163,7 +158,9 @@
 import org.apache.ozone.test.tag.Unhealthy;
 import org.apache.ratis.protocol.ClientId;
 import org.apache.ratis.util.ExitUtils;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
@@ -177,38 +174,64 @@ final class TestSecureOzoneCluster {
 
   private static final String COMPONENT = "om";
   private static final String OM_CERT_SERIAL_ID = "9879877970576";
+  private static final String CANNOT_AUTHENTICATE_MESSAGE = "Client cannot 
authenticate via:[KERBEROS]";
   private static final Logger LOG = LoggerFactory
       .getLogger(TestSecureOzoneCluster.class);
+  private static final int CERT_GRACE_TIME_MS = 10 * 1000; // 10s
+  private static final int DELEGATION_TOKEN_MAX_TIME_MS = 9 * 1000; // 9s
 
   @TempDir
-  private File tempDir;
+  private static File workDir;
+  private static MiniKdc miniKdc;
+  private static OzoneConfiguration kdcConf;
+  private static File scmKeytab;
+  private static File spnegoKeytab;
+  private static File omKeytab;
+  private static File testUserKeytab;
+  private static String testUserPrincipal;
+  private static String host;
+  private static String realm;
 
-  private MiniKdc miniKdc;
+  @TempDir
+  private File tempDir;
   private OzoneConfiguration conf;
-  private File workDir;
-  private File scmKeytab;
-  private File spnegoKeytab;
-  private File omKeyTab;
-  private File testUserKeytab;
-  private String testUserPrincipal;
   private StorageContainerManager scm;
   private ScmBlockLocationProtocol scmBlockClient;
   private OzoneManager om;
   private HddsProtos.OzoneManagerDetailsProto omInfo;
-  private String host;
   private String clusterId;
   private String scmId;
   private String omId;
   private OzoneManagerProtocolClientSideTranslatorPB omClient;
   private KeyPair keyPair;
   private Path omMetaDirPath;
-  private int certGraceTime = 10 * 1000; // 10s
-  private int delegationTokenMaxTime = 9 * 1000; // 9s
+
+  @BeforeAll
+  static void setupKdc() throws Exception {
+    ExitUtils.disableSystemExit();
+    DefaultMetricsSystem.setMiniClusterMode(true);
+    host = InetAddress.getLocalHost().getCanonicalHostName().toLowerCase();
+    startMiniKdc();
+    realm = miniKdc.getRealm();
+    testUserPrincipal = "test@" + realm;
+    scmKeytab = new File(workDir, "scm.keytab");
+    spnegoKeytab = new File(workDir, "http.keytab");
+    omKeytab = new File(workDir, "om.keytab");
+    testUserKeytab = new File(workDir, "testuser.keytab");
+    kdcConf = new OzoneConfiguration();
+    setSecureConfig(kdcConf);
+    createCredentialsInKDC(kdcConf);
+  }
+
+  @AfterAll
+  static void tearDownKdc() {
+    stopMiniKdc();
+  }
 
   @BeforeEach
   void init() {
     try {
-      conf = new OzoneConfiguration();
+      conf = new OzoneConfiguration(kdcConf);
       conf.set(OZONE_SCM_CLIENT_ADDRESS_KEY, "localhost");
 
       conf.setInt(OZONE_SCM_CLIENT_PORT_KEY, getFreePort());
@@ -220,34 +243,25 @@ void init() {
       conf.set(OZONE_OM_ADDRESS_KEY,
           InetAddress.getLocalHost().getCanonicalHostName() + ":" + 
getFreePort());
 
-      DefaultMetricsSystem.setMiniClusterMode(true);
-      ExitUtils.disableSystemExit();
       final String path = tempDir.getAbsolutePath();
       omMetaDirPath = Paths.get(path, "om-meta");
       conf.set(OZONE_METADATA_DIRS, omMetaDirPath.toString());
-      conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
-      conf.set(HADOOP_SECURITY_AUTHENTICATION, KERBEROS.name());
+
       conf.set(HDDS_X509_CA_ROTATION_CHECK_INTERNAL,
-          Duration.ofMillis(certGraceTime - 1000).toString());
+          Duration.ofMillis(CERT_GRACE_TIME_MS - 1000).toString());
       conf.set(HDDS_X509_RENEW_GRACE_DURATION,
-          Duration.ofMillis(certGraceTime).toString());
+          Duration.ofMillis(CERT_GRACE_TIME_MS).toString());
       conf.setBoolean(HDDS_X509_GRACE_DURATION_TOKEN_CHECKS_ENABLED, false);
-      conf.set(HDDS_X509_CA_ROTATION_CHECK_INTERNAL,
-          Duration.ofMillis(certGraceTime - 1000).toString());
       conf.set(HDDS_X509_CA_ROTATION_ACK_TIMEOUT,
-          Duration.ofMillis(certGraceTime - 1000).toString());
+          Duration.ofMillis(CERT_GRACE_TIME_MS - 1000).toString());
       conf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY,
-          delegationTokenMaxTime);
+          DELEGATION_TOKEN_MAX_TIME_MS);
 
-      workDir = new File(tempDir, "workdir");
       clusterId = UUID.randomUUID().toString();
       scmId = UUID.randomUUID().toString();
       omId = UUID.randomUUID().toString();
       scmBlockClient = new ScmBlockLocationTestingClient(null, null, 0);
 
-      startMiniKdc();
-      setSecureConfig();
-      createCredentialsInKDC();
       generateKeyPair();
       omInfo = OzoneManager.getOmDetailsProto(conf, omId);
     } catch (Exception e) {
@@ -258,182 +272,245 @@ void init() {
   @AfterEach
   void stop() throws Exception {
     try {
-      stopMiniKdc();
       if (scm != null) {
         scm.stop();
         scm.join();
       }
-      if (om != null) {
-        om.stop();
-        om.join();
-      }
-      IOUtils.closeQuietly(omClient);
     } catch (Exception e) {
       LOG.error("Failed to stop TestSecureOzoneCluster", e);
+    } finally {
+      IOUtils.closeQuietly(om);
+      IOUtils.closeQuietly(omClient);
     }
   }
 
-  private void createCredentialsInKDC() throws Exception {
-    ScmConfig scmConfig = conf.getObject(ScmConfig.class);
-    SCMHTTPServerConfig httpServerConfig =
-          conf.getObject(SCMHTTPServerConfig.class);
-    createPrincipal(scmKeytab, scmConfig.getKerberosPrincipal());
-    createPrincipal(spnegoKeytab, httpServerConfig.getKerberosPrincipal());
+  private static void createCredentialsInKDC(OzoneConfiguration conf) throws 
Exception {
+    createPrincipal(scmKeytab, conf.get(HDDS_SCM_KERBEROS_PRINCIPAL_KEY));
+    createPrincipal(spnegoKeytab, 
conf.get(HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY));
+    createPrincipal(omKeytab, conf.get(OZONE_OM_KERBEROS_PRINCIPAL_KEY));
     createPrincipal(testUserKeytab, testUserPrincipal);
-    createPrincipal(omKeyTab,
-        conf.get(OZONE_OM_KERBEROS_PRINCIPAL_KEY));
   }
 
-  private void createPrincipal(File keytab, String... principal)
+  private static void createPrincipal(File keytab, String... principal)
       throws Exception {
     miniKdc.createPrincipal(keytab, principal);
   }
 
-  private void startMiniKdc() throws Exception {
+  private static void startMiniKdc() throws Exception {
     Properties securityProperties = MiniKdc.createConf();
     miniKdc = new MiniKdc(securityProperties, workDir);
     miniKdc.start();
   }
 
-  private void stopMiniKdc() {
-    miniKdc.stop();
+  private static void stopMiniKdc() {
+    if (miniKdc != null) {
+      miniKdc.stop();
+    }
   }
 
-  private void setSecureConfig() throws IOException {
+  private static void setSecureConfig(OzoneConfiguration conf) throws 
IOException {
     conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
-    host = InetAddress.getLocalHost().getCanonicalHostName()
-        .toLowerCase();
-
     conf.set(HADOOP_SECURITY_AUTHENTICATION, "kerberos");
 
     String curUser = UserGroupInformation.getCurrentUser().getUserName();
     conf.set(OZONE_ADMINISTRATORS, curUser);
 
-    String realm = miniKdc.getRealm();
     String hostAndRealm = host + "@" + realm;
     conf.set(HDDS_SCM_KERBEROS_PRINCIPAL_KEY, "scm/" + hostAndRealm);
     conf.set(HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY, "HTTP_SCM/" + hostAndRealm);
     conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY, "om/" + hostAndRealm);
     conf.set(OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY, "HTTP_OM/" + hostAndRealm);
 
-    scmKeytab = new File(workDir, "scm.keytab");
-    spnegoKeytab = new File(workDir, "http.keytab");
-    omKeyTab = new File(workDir, "om.keytab");
-    testUserKeytab = new File(workDir, "testuser.keytab");
-    testUserPrincipal = "test@" + realm;
-
-    conf.set(HDDS_SCM_KERBEROS_KEYTAB_FILE_KEY,
-        scmKeytab.getAbsolutePath());
-    conf.set(HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY,
-        spnegoKeytab.getAbsolutePath());
-    conf.set(OZONE_OM_KERBEROS_KEYTAB_FILE_KEY,
-        omKeyTab.getAbsolutePath());
-    conf.set(OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE,
-        spnegoKeytab.getAbsolutePath());
+    conf.set(HDDS_SCM_KERBEROS_KEYTAB_FILE_KEY, scmKeytab.getAbsolutePath());
+    conf.set(HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY, 
spnegoKeytab.getAbsolutePath());
+    conf.set(OZONE_OM_KERBEROS_KEYTAB_FILE_KEY, omKeytab.getAbsolutePath());
+    conf.set(OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE, 
spnegoKeytab.getAbsolutePath());
   }
 
+  /**
+   * Exercises a secure SCM and OM sharing a single SCM instance to avoid 
paying
+   * the SCM startup cost twice. Covers SCM startup and cert trust chain, SCM
+   * security/admin access control, and secure OM initialization (the
+   * delegation-token/secret-key config-validation failure, the login success
+   * case, the Kerberos failure case, and re-initialization of an
+   * already-initialized OM).
+   */
   @Test
-  void testSecureScmStartupSuccess() throws Exception {
-
+  void testSecureScmAndOmStartupAndAccessControl() throws Exception {
     initSCM();
     scm = HddsTestUtils.getScmSimple(conf);
-    //Reads the SCM Info from SCM instance
-    try {
-      scm.start();
-      ScmInfo scmInfo = scm.getClientProtocolServer().getScmInfo();
-      assertEquals(clusterId, scmInfo.getClusterId());
-      assertEquals(scmId, scmInfo.getScmId());
-      assertEquals(2, scm.getScmCertificateClient().getTrustChain().size());
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
+    scm.start();
+
+    // SCM startup and access control.
+    assertScmExposesClusterInfoAndTrustChain();
+    assertScmSecurityProtocolAllowsKerberosUser();
+    assertScmSecurityProtocolRejectsNonKerberosUser();
+    assertScmAdminProtocolDeniesNonAdminUser();
+    assertScmAdminProtocolRejectsNonKerberosUser();
+
+    // Secure OM initialization against the shared SCM.
+    assertSecureOmInitFailsWithInvalidTokenAndSecretKeyConfig();
+    assertSecureOmInitSucceeds();
+    assertSecureOmInitFailsWithNonExistentPrincipal();
+    assertSecureOmInitWhenAlreadyInitialized();
+  }
+
+  /** SCM starts up and exposes its cluster info and cert trust chain. */
+  private void assertScmExposesClusterInfoAndTrustChain() throws IOException {
+    ScmInfo scmInfo = scm.getClientProtocolServer().getScmInfo();
+    assertEquals(clusterId, scmInfo.getClusterId());
+    assertEquals(scmId, scmInfo.getScmId());
+    assertEquals(2, scm.getScmCertificateClient().getTrustChain().size());
+  }
+
+  /** SCM security protocol - user with Kerberos credentials succeeds. */
+  private void assertScmSecurityProtocolAllowsKerberosUser() throws Exception {
+    UserGroupInformation ugi =
+        UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+            testUserPrincipal, testUserKeytab.getCanonicalPath());
+    ugi.setAuthenticationMethod(KERBEROS);
+    try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
+        getScmSecurityClient(conf, ugi)) {
+      assertNotNull(securityClient);
+      String caCert = securityClient.getCACertificate();
+      assertNotNull(caCert);
+      // Get some random certificate, used serial id 100 which will be
+      // unavailable as our serial id is time stamp. Serial id 1 is root CA,
+      // and it is persisted in DB.
+      SCMSecurityException securityException = assertThrows(
+          SCMSecurityException.class,
+          () -> securityClient.getCertificate("100"));
+      assertThat(securityException)
+          .hasMessageContaining("Certificate not found");
     }
   }
 
-  @Test
-  void testSCMSecurityProtocol() throws Exception {
+  /** SCM security protocol - user without Kerberos credentials fails. */
+  private void assertScmSecurityProtocolRejectsNonKerberosUser() throws 
Exception {
+    UserGroupInformation ugi = UserGroupInformation.createRemoteUser("test");
+    ugi.setAuthenticationMethod(AuthMethod.TOKEN);
+    try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
+        getScmSecurityClient(conf, ugi)) {
+      IOException ioException = assertThrows(IOException.class,
+          securityClient::getCACertificate);
+      
assertThat(ioException).hasMessageContaining(CANNOT_AUTHENTICATE_MESSAGE);
+      ioException = assertThrows(IOException.class,
+          () -> securityClient.getCertificate("1"));
+      
assertThat(ioException).hasMessageContaining(CANNOT_AUTHENTICATE_MESSAGE);
+    }
+  }
 
-    initSCM();
-    scm = HddsTestUtils.getScmSimple(conf);
-    //Reads the SCM Info from SCM instance
-    try {
-      scm.start();
+  /** SCM admin protocol - authenticated non-admin user is denied. */
+  private void assertScmAdminProtocolDeniesNonAdminUser() throws IOException {
+    UserGroupInformation ugi = 
UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+        testUserPrincipal, testUserKeytab.getCanonicalPath());
+    StorageContainerLocationProtocol scmRpcClient =
+        HAUtils.getScmContainerClient(conf, ugi);
+    IOException adminException = assertThrows(IOException.class,
+        scmRpcClient::forceExitSafeMode);
+    assertThat(adminException).hasMessageContaining("Access denied");
+  }
 
-      // Case 1: User with Kerberos credentials should succeed.
-      UserGroupInformation ugi =
-          UserGroupInformation.loginUserFromKeytabAndReturnUGI(
-              testUserPrincipal, testUserKeytab.getCanonicalPath());
-      ugi.setAuthenticationMethod(KERBEROS);
-      try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
-          getScmSecurityClient(conf, ugi)) {
-        assertNotNull(securityClient);
-        String caCert = securityClient.getCACertificate();
-        assertNotNull(caCert);
-        // Get some random certificate, used serial id 100 which will be
-        // unavailable as our serial id is time stamp. Serial id 1 is root CA,
-        // and it is persisted in DB.
-        SCMSecurityException securityException = assertThrows(
-            SCMSecurityException.class,
-            () -> securityClient.getCertificate("100"));
-        assertThat(securityException)
-            .hasMessageContaining("Certificate not found");
-      }
+  /** SCM admin protocol - user without Kerberos credentials fails. */
+  private void assertScmAdminProtocolRejectsNonKerberosUser() throws 
IOException {
+    UserGroupInformation ugi = UserGroupInformation.createRemoteUser("test");
+    ugi.setAuthenticationMethod(AuthMethod.TOKEN);
+    StorageContainerLocationProtocol scmRpcClient =
+        HAUtils.getScmContainerClient(conf, ugi);
+    IOException adminException = assertThrows(IOException.class,
+        scmRpcClient::forceExitSafeMode);
+    assertThat(adminException)
+        .hasMessageContaining(CANNOT_AUTHENTICATE_MESSAGE);
+  }
 
-      // Case 2: User without Kerberos credentials should fail.
-      ugi = UserGroupInformation.createRemoteUser("test");
-      ugi.setAuthenticationMethod(AuthMethod.TOKEN);
-      try (SCMSecurityProtocolClientSideTranslatorPB securityClient =
-          getScmSecurityClient(conf, ugi)) {
-
-        String cannotAuthMessage = "Client cannot authenticate via:[KERBEROS]";
-        IOException ioException = assertThrows(IOException.class,
-            securityClient::getCACertificate);
-        assertThat(ioException).hasMessageContaining(cannotAuthMessage);
-        ioException = assertThrows(IOException.class,
-            () -> securityClient.getCertificate("1"));
-        assertThat(ioException).hasMessageContaining(cannotAuthMessage);
-      }
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-    }
+  /**
+   * Secure OM initialization fails when the delegation token and secret key
+   * configuration don't meet requirement.
+   */
+  private void assertSecureOmInitFailsWithInvalidTokenAndSecretKeyConfig() {
+    conf.setTimeDuration(HDDS_SECRET_KEY_EXPIRY_DURATION, 7, TimeUnit.DAYS);
+    conf.setTimeDuration(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY, 7, 
TimeUnit.DAYS);
+    IllegalArgumentException exception = assertThrows(
+        IllegalArgumentException.class, () -> setupOm(conf));
+    assertThat(exception.getMessage()).contains("Secret key expiry duration 
hdds.secret.key.expiry.duration "  +
+        "should be greater than value of 
(ozone.manager.delegation.token.max-lifetime + " +
+        "ozone.manager.delegation.remover.scan.interval + 
hdds.secret.key.rotate.duration");
+    // Restore valid durations so the remaining cases can start OM.
+    conf.unset(HDDS_SECRET_KEY_EXPIRY_DURATION);
+    conf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY, 
DELEGATION_TOKEN_MAX_TIME_MS);
   }
 
-  @Test
-  void testAdminAccessControlException() throws Exception {
-    initSCM();
-    scm = HddsTestUtils.getScmSimple(conf);
-    //Reads the SCM Info from SCM instance
-    try {
-      scm.start();
+  /** Secure OM initialization succeeds. */
+  private void assertSecureOmInitSucceeds() throws Exception {
+    LogCapturer logs = LogCapturer.captureLogs(OzoneManager.class);
+    GenericTestUtils.setLogLevel(OzoneManager.class, INFO);
+    setupOm(conf);
+    assertThrows(Exception.class, om::start);
+    assertThat(logs.getOutput()).contains("Ozone Manager login successful");
+    logs.clearOutput();
+    stopOm();
+  }
 
-      //case 1: Run admin command with non-admin user.
-      UserGroupInformation ugi =
-          UserGroupInformation.loginUserFromKeytabAndReturnUGI(
-          testUserPrincipal, testUserKeytab.getCanonicalPath());
-      StorageContainerLocationProtocol scmRpcClient =
-          HAUtils.getScmContainerClient(conf, ugi);
-      IOException ioException = assertThrows(IOException.class,
-          scmRpcClient::forceExitSafeMode);
-      assertThat(ioException).hasMessageContaining("Access denied");
+  /**
+   * Secure OM initialization fails at Kerberos login for a non-existent
+   * principal. Storage is already initialized by
+   * {@link #assertSecureOmInitSucceeds()}, and createOm with a non-existent
+   * principal fails at Kerberos login before any storage check or RPC bind, so
+   * no additional setup is required.
+   */
+  private void assertSecureOmInitFailsWithNonExistentPrincipal() {
+    conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY,
+        "[email protected]");
+    testCommonKerberosFailures(() -> OzoneManager.createOm(conf));
+  }
 
-      // Case 2: User without Kerberos credentials should fail.
-      ugi = UserGroupInformation.createRemoteUser("test");
-      ugi.setAuthenticationMethod(AuthMethod.TOKEN);
-      scmRpcClient =
-          HAUtils.getScmContainerClient(conf, ugi);
+  /**
+   * Secure OM can be re-initialized when it is already initialized.
+   * The failure cases above left a non-existent principal and an invalid auth
+   * method on conf; restore the valid values before re-initializing. A fresh
+   * RPC port is also required: {@link #assertSecureOmInitSucceeds()}'s start()
+   * failed before starting the RPC server, so its listener selector loop never
+   * ran and stop() cannot release the bound port.
+   */
+  private void assertSecureOmInitWhenAlreadyInitialized() throws Exception {
+    conf.set(HADOOP_SECURITY_AUTHENTICATION, "kerberos");
+    conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY, "om/" + host + "@" + realm);
+    conf.set(OZONE_OM_ADDRESS_KEY,
+        InetAddress.getLocalHost().getCanonicalHostName() + ":" + 
getFreePort());
+    LogCapturer omLogs = LogCapturer.captureLogs(OMCertificateClient.class);
+    omLogs.clearOutput();
+    conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, false);
+    OMStorage omStore = new OMStorage(conf);
+    initializeOmStorage(omStore);
+    OzoneManager.setTestSecureOmFlag(true);
+    om = OzoneManager.createOm(conf);
 
-      String cannotAuthMessage = "Client cannot authenticate via:[KERBEROS]";
-      ioException = assertThrows(IOException.class,
-          scmRpcClient::forceExitSafeMode);
-      assertThat(ioException).hasMessageContaining(cannotAuthMessage);
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-    }
+    assertNull(om.getCertificateClient());
+    String logOutput = omLogs.getOutput();
+    assertThat(logOutput)
+        .doesNotContain("Init response: GETCERT");
+    assertThat(logOutput)
+        .doesNotContain("Successfully stored SCM signed certificate");
+
+    stopOm();
+
+    conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
+    conf.setBoolean(OZONE_OM_S3_GPRC_SERVER_ENABLED, true);
+    conf.set(OZONE_OM_ADDRESS_KEY,
+        InetAddress.getLocalHost().getCanonicalHostName() + ":" + 
getFreePort());
+
+    OzoneManager.omInit(conf);
+    om = OzoneManager.createOm(conf);
+
+    assertNotNull(om.getCertificateClient());
+    assertNotNull(om.getCertificateClient().getPublicKey());
+    assertNotNull(om.getCertificateClient().getPrivateKey());
+    assertNotNull(om.getCertificateClient().getCertificate());
+    assertThat(omLogs.getOutput())
+        .contains("Init response: GETCERT")
+        .contains("Successfully stored OM signed certificate");
+    X509Certificate certificate = om.getCertificateClient().getCertificate();
+    validateCertificate(certificate);
   }
 
   private void initSCM() throws IOException {
@@ -504,97 +581,19 @@ private void testCommonKerberosFailures(Callable<?> test) 
{
         .hasMessageContaining("KERBEROS_SSL authentication method not");
   }
 
-  /**
-   * Tests the secure om Initialization Failure.
-   */
   @Test
-  void testSecureOMInitializationFailure() throws Exception {
-    initSCM();
-    // Create a secure SCM instance as om client will connect to it
-    scm = HddsTestUtils.getScmSimple(conf);
-    try {
-      scm.start();
-      setupOm(conf);
-      conf.set(OZONE_OM_KERBEROS_PRINCIPAL_KEY,
-          "[email protected]");
-      testCommonKerberosFailures(() -> OzoneManager.createOm(conf));
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-    }
-  }
-
-  /**
-   * Tests the secure om Initialization Failure due to delegation token and 
secret key configuration don't meet
-   * requirement.
-   */
-  @Test
-  void testSecureOMDelegationTokenSecretManagerInitializationFailure() throws 
Exception {
-    initSCM();
-    // Create a secure SCM instance as om client will connect to it
-    scm = HddsTestUtils.getScmSimple(conf);
-    try {
-      scm.start();
-      conf.setTimeDuration(HDDS_SECRET_KEY_EXPIRY_DURATION, 7, TimeUnit.DAYS);
-      conf.setTimeDuration(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY, 7, 
TimeUnit.DAYS);
-      IllegalArgumentException exception = assertThrows(
-          IllegalArgumentException.class, () -> setupOm(conf));
-      assertThat(exception.getMessage()).contains("Secret key expiry duration 
hdds.secret.key.expiry.duration "  +
-          "should be greater than value of 
(ozone.manager.delegation.token.max-lifetime + " +
-          "ozone.manager.delegation.remover.scan.interval + 
hdds.secret.key.rotate.duration");
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-    }
-  }
-
-  /**
-   * Tests the secure om Initialization success.
-   */
-  @Test
-  void testSecureOmInitializationSuccess() throws Exception {
+  void testAccessControlExceptionOnClient() throws Exception {
     initSCM();
     // Create a secure SCM instance as om client will connect to it
     scm = HddsTestUtils.getScmSimple(conf);
-    LogCapturer logs = LogCapturer.captureLogs(OzoneManager.class);
-    GenericTestUtils.setLogLevel(OzoneManager.class, INFO);
-
-    try {
-      scm.start();
-      setupOm(conf);
-      om.start();
-    } catch (Exception ex) {
-      // Expects timeout failure from scmClient in om but om user login via
-      // kerberos should succeed.
-      assertThat(logs.getOutput()).contains("Ozone Manager login successful");
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-    }
-  }
+    scm.start();
 
-  @Test
-  void testAccessControlExceptionOnClient() throws Exception {
-    initSCM();
-    LogCapturer logs = LogCapturer.captureLogs(OzoneManager.class);
-    GenericTestUtils.setLogLevel(OzoneManager.class, INFO);
-    try {
-      // Create a secure SCM instance as om client will connect to it
-      scm = HddsTestUtils.getScmSimple(conf);
-      scm.start();
+    setupOm(conf);
+    om.setCertClient(new CertificateClientTestImpl(conf));
+    om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
+    om.start();
 
-      setupOm(conf);
-      om.setCertClient(new CertificateClientTestImpl(conf));
-      om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
-      om.start();
-    } catch (Exception ex) {
-      // Expects timeout failure from scmClient in om but om user login via
-      // kerberos should succeed.
-      assertThat(logs.getOutput()).contains("Ozone Manager login successful");
-    }
+    // positive case (happy-path)
     UserGroupInformation ugi =
         UserGroupInformation.loginUserFromKeytabAndReturnUGI(
             testUserPrincipal, testUserKeytab.getCanonicalPath());
@@ -609,6 +608,7 @@ void testAccessControlExceptionOnClient() throws Exception {
             .setAdminName("admin")
             .build());
 
+    // negative (an unauthenticated client gets rejected)
     ugi = UserGroupInformation.createUserForTesting(
         "testuser1", new String[] {"test"});
 
@@ -618,7 +618,7 @@ void testAccessControlExceptionOnClient() throws Exception {
             ClientId.randomId().toString());
     String exMessage = "org.apache.hadoop.security.AccessControlException: " +
         "Client cannot authenticate via:[TOKEN, KERBEROS]";
-    logs = LogCapturer.captureLogs(Client.class);
+    LogCapturer logs = LogCapturer.captureLogs(Client.class);
     IOException ioException = assertThrows(IOException.class,
         () -> unsecureClient.listAllVolumes(null, null, 0));
     assertThat(ioException).hasMessageContaining(exMessage);
@@ -634,304 +634,145 @@ private void generateKeyPair() throws Exception {
     keyStorage.storeKeyPair(keyPair);
   }
 
-  /**
-   * Tests delegation token renewal.
-   */
-  @Test
-  void testDelegationTokenRenewal() throws Exception {
-    GenericTestUtils.setLogLevel(Server.class, INFO);
-    LogCapturer omLogs = LogCapturer.captureLogs(OzoneManager.class);
-
-    // Setup SCM
-    initSCM();
-    scm = HddsTestUtils.getScmSimple(conf);
-    try {
-      // Start SCM
-      scm.start();
-
-      // Setup secure OM for start.  Generous token lifetime so the renewer and
-      // tampered-token cases below do not race token expiry.
-      conf.setLong(DELEGATION_TOKEN_MAX_LIFETIME_KEY, 60 * 1000L);
-      setupOm(conf);
-      OzoneManager.setTestSecureOmFlag(true);
-      om.setCertClient(new CertificateClientTestImpl(conf));
-      om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
-      om.start();
-
-      UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
-
-      // Get first OM client which will authenticate via Kerberos
-      omClient = new OzoneManagerProtocolClientSideTranslatorPB(
-          OmTransportFactory.create(conf, ugi, null),
-          RandomStringUtils.secure().nextAscii(5));
-
-      // Since client is already connected get a delegation token
-      Token<OzoneTokenIdentifier> token = omClient.getDelegationToken(
-          new Text("om"));
-
-      // Check if token is of right kind and renewer is running om instance
-      assertNotNull(token);
-      assertEquals("OzoneToken", token.getKind().toString());
-      assertEquals(SecurityUtil.buildTokenService(
-          om.getNodeDetails().getRpcAddress()).toString(),
-          token.getService().toString());
-
-      // Renew delegation token
-      long expiryTime = omClient.renewDelegationToken(token);
-      assertThat(expiryTime).isGreaterThan(0);
-      omLogs.clearOutput();
-
-      // Test failure of delegation renewal
-      // 1. When token maxExpiryTime exceeds (maxDate in the past)
-      OzoneTokenIdentifier expiredId = OzoneTokenIdentifier.readProtoBuf(
-          token.getIdentifier());
-      expiredId.setMaxDate(System.currentTimeMillis() - 1000);
-      Token<OzoneTokenIdentifier> expiredToken = new Token<>(
-          expiredId.getBytes(), token.getPassword(), token.getKind(),
-          token.getService());
-      OMException ex = assertThrows(OMException.class,
-          () -> omClient.renewDelegationToken(expiredToken));
-      assertEquals(TOKEN_EXPIRED, ex.getResult());
-      omLogs.clearOutput();
-
-      // 2. When renewer doesn't match (implicitly covers when renewer is
-      // null or empty )
-      Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
-          new Text("randomService"));
-      assertNotNull(token2);
-      ex = assertThrows(OMException.class,
-          () -> omClient.renewDelegationToken(token2));
-      assertThat(ex).hasMessageContaining("Delegation token renewal failed");
-      assertThat(omLogs.getOutput()).contains(" with non-matching renewer 
randomService");
-      omLogs.clearOutput();
-
-      // 3. Test tampered token
-      OzoneTokenIdentifier tokenId = OzoneTokenIdentifier.readProtoBuf(
-          token.getIdentifier());
-      tokenId.setRenewer(new Text("om"));
-      tokenId.setMaxDate(System.currentTimeMillis() * 2);
-      Token<OzoneTokenIdentifier> tamperedToken = new Token<>(
-          tokenId.getBytes(), token2.getPassword(), token2.getKind(),
-          token2.getService());
-      ex = assertThrows(OMException.class,
-          () -> omClient.renewDelegationToken(tamperedToken));
-      assertThat(ex).hasMessageContaining("Delegation token renewal failed");
-      assertThat(omLogs.getOutput()).contains("can't be found in cache");
-      omLogs.clearOutput();
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-      IOUtils.closeQuietly(om);
-    }
-  }
-
   private void setupOm(OzoneConfiguration config) throws Exception {
     OMStorage omStore = new OMStorage(config);
-    omStore.setClusterId(clusterId);
-    omStore.setOmCertSerialId(OM_CERT_SERIAL_ID);
-    // writes the version file properties
-    omStore.initialize();
+    if (omStore.getState() != Storage.StorageState.INITIALIZED) {
+      omStore.setClusterId(clusterId);
+      omStore.setOmCertSerialId(OM_CERT_SERIAL_ID);
+      // writes the version file properties
+      omStore.initialize();
+    }
     OzoneManager.setTestSecureOmFlag(true);
     om = OzoneManager.createOm(config);
   }
 
-  @Test
-  @Flaky("HDDS-9349")
-  void testGetSetRevokeS3Secret() throws Exception {
-    initSCM();
-    try {
-      scm = HddsTestUtils.getScmSimple(conf);
-      scm.start();
-
-      // Setup secure OM for start
-      setupOm(conf);
-      // Start OM
-      om.setCertClient(new CertificateClientTestImpl(conf));
-      om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
-      om.start();
-      UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
-      String username = ugi.getUserName();
-
-      // Get first OM client which will authenticate via Kerberos
-      omClient = new OzoneManagerProtocolClientSideTranslatorPB(
-          OmTransportFactory.create(conf, ugi, null),
-          RandomStringUtils.secure().nextAscii(5));
-
-      // Creates a secret since it does not exist
-      S3SecretValue attempt1 = omClient.getS3Secret(username);
-
-      // A second getS3Secret on the same username should throw exception
-      try {
-        omClient.getS3Secret(username);
-      } catch (OMException omEx) {
-        assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
-            omEx.getResult());
-      }
-
-      // Revoke the existing secret
-      omClient.revokeS3Secret(username);
-
-      // Set secret should fail since the accessId is revoked
-      final String secretKeySet = "somesecret1";
-      try {
-        omClient.setS3Secret(username, secretKeySet);
-      } catch (OMException omEx) {
-        assertEquals(OMException.ResultCodes.ACCESS_ID_NOT_FOUND,
-            omEx.getResult());
-      }
-
-      // Get a new secret
-      S3SecretValue attempt3 = omClient.getS3Secret(username);
-
-      // secret should differ because it has been revoked previously
-      assertNotEquals(attempt3.getAwsSecret(), attempt1.getAwsSecret());
-
-      // accessKey is still the same because it is derived from username
-      assertEquals(attempt3.getAwsAccessKey(), attempt1.getAwsAccessKey());
-
-      // Admin can set secret for any user
-      S3SecretValue attempt4 = omClient.setS3Secret(username, secretKeySet);
-      assertEquals(secretKeySet, attempt4.getAwsSecret());
-
-      // A second getS3Secret on the same username should throw exception
-      try {
-        omClient.getS3Secret(username);
-      } catch (OMException omEx) {
-        assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
-            omEx.getResult());
-      }
-
-      // Clean up
-      omClient.revokeS3Secret(username);
-
-      // Admin can get and revoke other users' secrets
-      // omClient's ugi is current user, which is added as an OM admin
-      omClient.getS3Secret("HADOOP/ALICE");
-      omClient.revokeS3Secret("HADOOP/ALICE");
-
-      // testUser is not an admin
-      final UserGroupInformation ugiNonAdmin =
-          UserGroupInformation.loginUserFromKeytabAndReturnUGI(
-              testUserPrincipal, testUserKeytab.getCanonicalPath());
-      final OzoneManagerProtocolClientSideTranslatorPB omClientNonAdmin =
-          new OzoneManagerProtocolClientSideTranslatorPB(
-          OmTransportFactory.create(conf, ugiNonAdmin, null),
-          RandomStringUtils.secure().nextAscii(5));
-
-      OMException omException = assertThrows(OMException.class,
-          () -> omClientNonAdmin.getS3Secret("HADOOP/JOHN"));
-      assertSame(USER_MISMATCH, omException.getResult());
-      omException = assertThrows(OMException.class,
-          () -> omClientNonAdmin.revokeS3Secret("HADOOP/DOE"));
-      assertSame(USER_MISMATCH, omException.getResult());
-
-    } finally {
-      if (scm != null) {
-        scm.stop();
+  private void stopOm() {
+    if (om != null) {
+      if (om.stop()) {
+        om.join();
       }
-      IOUtils.closeQuietly(om);
+      om = null;
     }
   }
 
-  /**
-   * Tests functionality to init secure OM when it is already initialized.
-   */
   @Test
-  void testSecureOmReInit() throws Exception {
-    LogCapturer omLogs = LogCapturer.captureLogs(OMCertificateClient.class);
-    omLogs.clearOutput();
-
+  @Flaky("HDDS-9349")
+  void testGetSetRevokeS3Secret() throws Exception {
     initSCM();
-    try {
-      scm = HddsTestUtils.getScmSimple(conf);
-      scm.start();
-      conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, false);
-      OMStorage omStore = new OMStorage(conf);
-      initializeOmStorage(omStore);
-      OzoneManager.setTestSecureOmFlag(true);
-      om = OzoneManager.createOm(conf);
-
-      assertNull(om.getCertificateClient());
-      String logOutput = omLogs.getOutput();
-      assertThat(logOutput)
-          .doesNotContain("Init response: GETCERT");
-      assertThat(logOutput)
-          .doesNotContain("Successfully stored SCM signed certificate");
-
-      if (om.stop()) {
-        om.join();
-      }
-
-      conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
-      conf.setBoolean(OZONE_OM_S3_GPRC_SERVER_ENABLED, true);
-      conf.set(OZONE_OM_ADDRESS_KEY,
-          InetAddress.getLocalHost().getCanonicalHostName() + ":" + 
getFreePort());
-
-      OzoneManager.omInit(conf);
-      om = OzoneManager.createOm(conf);
-
-      assertNotNull(om.getCertificateClient());
-      assertNotNull(om.getCertificateClient().getPublicKey());
-      assertNotNull(om.getCertificateClient().getPrivateKey());
-      assertNotNull(om.getCertificateClient().getCertificate());
-      assertThat(omLogs.getOutput())
-          .contains("Init response: GETCERT")
-          .contains("Successfully stored OM signed certificate");
-      X509Certificate certificate = om.getCertificateClient().getCertificate();
-      validateCertificate(certificate);
-
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-    }
+    scm = HddsTestUtils.getScmSimple(conf);
+    scm.start();
+
+    // Setup secure OM for start
+    setupOm(conf);
+    // Start OM
+    om.setCertClient(new CertificateClientTestImpl(conf));
+    om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
+    om.start();
+    UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+    String username = ugi.getUserName();
+
+    // Get first OM client which will authenticate via Kerberos
+    omClient = new OzoneManagerProtocolClientSideTranslatorPB(
+        OmTransportFactory.create(conf, ugi, null),
+        RandomStringUtils.secure().nextAscii(5));
+
+    // Creates a secret since it does not exist
+    S3SecretValue attempt1 = omClient.getS3Secret(username);
+
+    // A second getS3Secret on the same username should throw exception
+    OMException omEx = assertThrows(OMException.class,
+        () -> omClient.getS3Secret(username));
+    assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
+        omEx.getResult());
+
+    // Revoke the existing secret
+    omClient.revokeS3Secret(username);
+
+    // Set secret should fail since the accessId is revoked
+    final String secretKeySet = "somesecret1";
+    omEx = assertThrows(OMException.class,
+        () -> omClient.setS3Secret(username, secretKeySet));
+    assertEquals(OMException.ResultCodes.ACCESS_ID_NOT_FOUND,
+        omEx.getResult());
+
+    // Get a new secret
+    S3SecretValue attempt3 = omClient.getS3Secret(username);
+
+    // secret should differ because it has been revoked previously
+    assertNotEquals(attempt3.getAwsSecret(), attempt1.getAwsSecret());
+
+    // accessKey is still the same because it is derived from username
+    assertEquals(attempt3.getAwsAccessKey(), attempt1.getAwsAccessKey());
+
+    // Admin can set secret for any user
+    S3SecretValue attempt4 = omClient.setS3Secret(username, secretKeySet);
+    assertEquals(secretKeySet, attempt4.getAwsSecret());
+
+    // A second getS3Secret on the same username should throw exception
+    omEx = assertThrows(OMException.class,
+        () -> omClient.getS3Secret(username));
+    assertEquals(OMException.ResultCodes.S3_SECRET_ALREADY_EXISTS,
+        omEx.getResult());
+
+    // Clean up
+    omClient.revokeS3Secret(username);
+
+    // Admin can get and revoke other users' secrets
+    // omClient's ugi is current user, which is added as an OM admin
+    omClient.getS3Secret("HADOOP/ALICE");
+    omClient.revokeS3Secret("HADOOP/ALICE");
+
+    // testUser is not an admin
+    final UserGroupInformation ugiNonAdmin =
+        UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+            testUserPrincipal, testUserKeytab.getCanonicalPath());
+    final OzoneManagerProtocolClientSideTranslatorPB omClientNonAdmin =
+        new OzoneManagerProtocolClientSideTranslatorPB(
+        OmTransportFactory.create(conf, ugiNonAdmin, null),
+        RandomStringUtils.secure().nextAscii(5));
+
+    OMException omException = assertThrows(OMException.class,
+        () -> omClientNonAdmin.getS3Secret("HADOOP/JOHN"));
+    assertSame(USER_MISMATCH, omException.getResult());
+    omException = assertThrows(OMException.class,
+        () -> omClientNonAdmin.revokeS3Secret("HADOOP/DOE"));
+    assertSame(USER_MISMATCH, omException.getResult());
   }
 
   /**
    * Test functionality to get SCM signed certificate for OM.
    */
   @Test
-  void testSecureOmInitSuccess() throws Exception {
+  void testSecureOmGetsScmSignedCertificate() throws Exception {
     LogCapturer omLogs =
         LogCapturer.captureLogs(OMCertificateClient.class);
     omLogs.clearOutput();
     initSCM();
-    try {
-      scm = HddsTestUtils.getScmSimple(conf);
-      scm.start();
-
-      OMStorage omStore = new OMStorage(conf);
-      initializeOmStorage(omStore);
-      OzoneManager.setTestSecureOmFlag(true);
-      om = OzoneManager.createOm(conf);
+    scm = HddsTestUtils.getScmSimple(conf);
+    scm.start();
 
-      assertNotNull(om.getCertificateClient());
-      assertNotNull(om.getCertificateClient().getPublicKey());
-      assertNotNull(om.getCertificateClient().getPrivateKey());
-      assertNotNull(om.getCertificateClient().getCertificate());
-      assertEquals(3, om.getCertificateClient().getTrustChain().size());
-      assertThat(omLogs.getOutput())
-          .contains("Init response: GETCERT")
-          .contains("Successfully stored OM signed certificate");
-      X509Certificate certificate = om.getCertificateClient().getCertificate();
-      validateCertificate(certificate);
-      String pemEncodedCACert =
-          scm.getSecurityProtocolServer().getCACertificate();
-      X509Certificate caCert =
-          CertificateCodec.getX509Certificate(pemEncodedCACert);
-      X509Certificate caCertStored = om.getCertificateClient()
-          .getCertificate(caCert.getSerialNumber().toString());
-      assertEquals(caCert, caCertStored);
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-      if (om != null) {
-        om.stop();
-      }
-      IOUtils.closeQuietly(om);
-    }
+    OMStorage omStore = new OMStorage(conf);
+    initializeOmStorage(omStore);
+    OzoneManager.setTestSecureOmFlag(true);
+    om = OzoneManager.createOm(conf);
+
+    assertNotNull(om.getCertificateClient());
+    assertNotNull(om.getCertificateClient().getPublicKey());
+    assertNotNull(om.getCertificateClient().getPrivateKey());
+    assertNotNull(om.getCertificateClient().getCertificate());
+    assertEquals(3, om.getCertificateClient().getTrustChain().size());
+    assertThat(omLogs.getOutput())
+        .contains("Init response: GETCERT")
+        .contains("Successfully stored OM signed certificate");
+    X509Certificate certificate = om.getCertificateClient().getCertificate();
+    validateCertificate(certificate);
+    String pemEncodedCACert =
+        scm.getSecurityProtocolServer().getCACertificate();
+    X509Certificate caCert =
+        CertificateCodec.getX509Certificate(pemEncodedCACert);
+    X509Certificate caCertStored = om.getCertificateClient()
+        .getCertificate(caCert.getSerialNumber().toString());
+    assertEquals(caCert, caCertStored);
   }
 
   /**
@@ -1112,7 +953,6 @@ void testCertificateRotationUnRecoverableFailure() throws 
Exception {
     LogCapturer certClientLogs = 
LogCapturer.captureLogs(OMCertificateClient.class);
     LogCapturer exitUtilLog = LogCapturer.captureLogs(ExitUtil.class);
 
-
     OMStorage omStorage = new OMStorage(conf);
     omStorage.setClusterId(clusterId);
     omStorage.setOmId(omId);
@@ -1172,77 +1012,70 @@ public String renewAndStoreKeyAndCertificate(boolean 
force) throws CertificateEx
   @Test
   void testDelegationTokenRenewCrossSecretKeyRotation() throws Exception {
     initSCM();
-    try {
-      scm = HddsTestUtils.getScmSimple(conf);
-      scm.start();
-
-      // Setup secure OM for start.
-      final int certLifetime = 40 * 1000; // 40s
-      OzoneConfiguration newConf = new OzoneConfiguration(conf);
-      newConf.set(HDDS_X509_DEFAULT_DURATION,
-          Duration.ofMillis(certLifetime).toString());
-      newConf.set(HDDS_X509_RENEW_GRACE_DURATION,
-          Duration.ofMillis(certLifetime - 15 * 1000).toString());
-      newConf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY,
-          certLifetime - 20 * 1000);
-
-      setupOm(newConf);
-      OzoneManager.setTestSecureOmFlag(true);
-
-      CertificateClientTestImpl certClient =
-          new CertificateClientTestImpl(newConf, true);
-      // Start OM
-      om.setCertClient(certClient);
-      om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
-      SecretKeyTestClient secretKeyClient = new SecretKeyTestClient();
-      ManagedSecretKey secretKey1 = secretKeyClient.getCurrentSecretKey();
-      om.setSecretKeyClient(secretKeyClient);
-      om.start();
-      GenericTestUtils.waitFor(() -> om.isLeaderReady(), 100, 10000);
-
-      UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+    scm = HddsTestUtils.getScmSimple(conf);
+    scm.start();
+
+    // Setup secure OM for start.
+    final int certLifetime = 40 * 1000; // 40s
+    OzoneConfiguration newConf = new OzoneConfiguration(conf);
+    newConf.set(HDDS_X509_DEFAULT_DURATION,
+        Duration.ofMillis(certLifetime).toString());
+    newConf.set(HDDS_X509_RENEW_GRACE_DURATION,
+        Duration.ofMillis(certLifetime - 15 * 1000).toString());
+    newConf.setLong(OMConfigKeys.DELEGATION_TOKEN_MAX_LIFETIME_KEY,
+        certLifetime - 20 * 1000);
+
+    setupOm(newConf);
+    OzoneManager.setTestSecureOmFlag(true);
 
-      // Get first OM client which will authenticate via Kerberos
-      omClient = new OzoneManagerProtocolClientSideTranslatorPB(
-          OmTransportFactory.create(newConf, ugi, null),
-          RandomStringUtils.secure().nextAscii(5));
-
-      // Since client is already connected get a delegation token
-      Token<OzoneTokenIdentifier> token1 = omClient.getDelegationToken(
-          new Text("om"));
-
-      // Check if token is of right kind and renewer is running om instance
-      assertNotNull(token1);
-      assertEquals("OzoneToken", token1.getKind().toString());
-      assertEquals(SecurityUtil.buildTokenService(
-          om.getNodeDetails().getRpcAddress()).toString(),
-          token1.getService().toString());
-      assertEquals(secretKey1.getId().toString(), 
token1.decodeIdentifier().getSecretKeyId());
-
-      // Renew delegation token
-      long expiryTime = omClient.renewDelegationToken(token1);
-      assertThat(expiryTime).isGreaterThan(0);
-
-      // Rotate secret key
-      secretKeyClient.rotate();
-      ManagedSecretKey secretKey2 = secretKeyClient.getCurrentSecretKey();
-      assertNotEquals(secretKey1.getId(), secretKey2.getId());
-      // Get a new delegation token
-      Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
-          new Text("om"));
-      assertEquals(secretKey2.getId().toString(), 
token2.decodeIdentifier().getSecretKeyId());
-
-      // Because old secret key is still valid, so renew old token will succeed
-      expiryTime = omClient.renewDelegationToken(token1);
-      assertThat(expiryTime)
-          .isGreaterThan(0)
-          .isLessThan(secretKey2.getExpiryTime().toEpochMilli());
-    } finally {
-      if (scm != null) {
-        scm.stop();
-      }
-      IOUtils.closeQuietly(om);
-    }
+    CertificateClientTestImpl certClient =
+        new CertificateClientTestImpl(newConf, true);
+    // Start OM
+    om.setCertClient(certClient);
+    om.setScmTopologyClient(new ScmTopologyClient(scmBlockClient));
+    SecretKeyTestClient secretKeyClient = new SecretKeyTestClient();
+    ManagedSecretKey secretKey1 = secretKeyClient.getCurrentSecretKey();
+    om.setSecretKeyClient(secretKeyClient);
+    om.start();
+    GenericTestUtils.waitFor(() -> om.isLeaderReady(), 100, 10000);
+
+    UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
+
+    // Get first OM client which will authenticate via Kerberos
+    omClient = new OzoneManagerProtocolClientSideTranslatorPB(
+        OmTransportFactory.create(newConf, ugi, null),
+        RandomStringUtils.secure().nextAscii(5));
+
+    // Since client is already connected get a delegation token
+    Token<OzoneTokenIdentifier> token1 = omClient.getDelegationToken(
+        new Text("om"));
+
+    // Check if token is of right kind and renewer is running om instance
+    assertNotNull(token1);
+    assertEquals("OzoneToken", token1.getKind().toString());
+    assertEquals(SecurityUtil.buildTokenService(
+        om.getNodeDetails().getRpcAddress()).toString(),
+        token1.getService().toString());
+    assertEquals(secretKey1.getId().toString(), 
token1.decodeIdentifier().getSecretKeyId());
+
+    // Renew delegation token
+    long expiryTime = omClient.renewDelegationToken(token1);
+    assertThat(expiryTime).isGreaterThan(0);
+
+    // Rotate secret key
+    secretKeyClient.rotate();
+    ManagedSecretKey secretKey2 = secretKeyClient.getCurrentSecretKey();
+    assertNotEquals(secretKey1.getId(), secretKey2.getId());
+    // Get a new delegation token
+    Token<OzoneTokenIdentifier> token2 = omClient.getDelegationToken(
+        new Text("om"));
+    assertEquals(secretKey2.getId().toString(), 
token2.decodeIdentifier().getSecretKeyId());
+
+    // Because old secret key is still valid, so renew old token will succeed
+    expiryTime = omClient.renewDelegationToken(token1);
+    assertThat(expiryTime)
+        .isGreaterThan(0)
+        .isLessThan(secretKey2.getExpiryTime().toEpochMilli());
   }
 
   /**


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

Reply via email to