HDDS-4952. Implement listCAs and getRootCA API. (#2042)

diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java
index 534cf7a..d75acc4 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/security/exception/SCMSecurityException.java
@@ -107,6 +107,7 @@
     INTERNAL_ERROR,
     DEFAULT,
     MISSING_BLOCK_TOKEN,
-    BLOCK_TOKEN_VERIFICATION_FAILED
+    BLOCK_TOKEN_VERIFICATION_FAILED,
+    GET_ROOT_CA_CERT_FAILED
   }
 }
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java
index 4a59570..778a6d4 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SCMSecurityProtocol.java
@@ -105,4 +105,31 @@
   List<String> listCertificate(HddsProtos.NodeType type, long startSerialId,
       int count, boolean isRevoked) throws IOException;
 
+  /**
+   * Get Root CA certificate.
+   * @return
+   * @throws IOException
+   */
+  String getRootCACertificate() throws IOException;
+
+  /**
+   * Returns all the individual SCM CA's along with Root CA.
+   *
+   * For example 3 nodes SCM HA cluster, the output will be
+   *
+   * SCM1 CA
+   * SCM2 CA
+   * SCM3 CA
+   * Root CA
+   * @return list of CA's
+   *
+   * For example on non-HA cluster the output will be SCM CA and Root CA.
+   *
+   * SCM CA
+   * Root CA
+   *
+   * @throws IOException
+   */
+  List<String> listCACertificate() throws IOException;
+
 }
diff --git a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java
index f54d228..0ab5d7e 100644
--- a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java
+++ b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SCMSecurityProtocolClientSideTranslatorPB.java
@@ -33,6 +33,7 @@
 import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertResponseProto;
 import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetCertificateRequestProto;
 import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMGetDataNodeCertRequestProto;
+import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMListCACertificateRequestProto;
 import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMListCertificateRequestProto;
 import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMSecurityRequest;
 import org.apache.hadoop.hdds.protocol.proto.SCMSecurityProtocolProtos.SCMSecurityRequest.Builder;
@@ -296,6 +297,24 @@
         .getListCertificateResponseProto().getCertificatesList();
   }
 
+  @Override
+  public String getRootCACertificate() throws IOException {
+    SCMGetCACertificateRequestProto protoIns = SCMGetCACertificateRequestProto
+        .getDefaultInstance();
+    return submitRequest(Type.GetCACertificate,
+        builder -> builder.setGetCACertificateRequest(protoIns))
+        .getGetCertResponseProto().getX509RootCACertificate();
+  }
+
+  @Override
+  public List<String> listCACertificate() throws IOException {
+    SCMListCACertificateRequestProto proto =
+        SCMListCACertificateRequestProto.getDefaultInstance();
+    return submitRequest(Type.ListCACertificate,
+        builder -> builder.setListCACertificateRequestProto(proto))
+        .getListCertificateResponseProto().getCertificatesList();
+  }
+
   /**
    * Return the proxy object underlying this protocol translator.
    *
diff --git a/hadoop-hdds/interface-server/src/main/proto/ScmServerSecurityProtocol.proto b/hadoop-hdds/interface-server/src/main/proto/ScmServerSecurityProtocol.proto
index 48c6cf9..31aac90 100644
--- a/hadoop-hdds/interface-server/src/main/proto/ScmServerSecurityProtocol.proto
+++ b/hadoop-hdds/interface-server/src/main/proto/ScmServerSecurityProtocol.proto
@@ -50,6 +50,7 @@
     optional SCMGetCACertificateRequestProto getCACertificateRequest = 6;
     optional SCMListCertificateRequestProto listCertificateRequest = 7;
     optional SCMGetSCMCertRequestProto getSCMCertificateRequest = 8;
+    optional SCMListCACertificateRequestProto listCACertificateRequestProto = 9;
 
 }
 
@@ -79,6 +80,8 @@
     GetCACertificate = 4;
     ListCertificate = 5;
     GetSCMCertificate = 6;
+    GetRootCACertificate = 7;
+    ListCACertificate = 8;
 }
 
 enum Status {
@@ -96,6 +99,7 @@
     DEFAULT = 12;
     MISSING_BLOCK_TOKEN = 13;
     BLOCK_TOKEN_VERIFICATION_FAILED = 14;
+    GET_ROOT_CA_CERTIFICATE_FAILED = 15;
 }
 /**
 * This message is send by data node to prove its identity and get an SCM
@@ -155,6 +159,8 @@
     required ResponseCode responseCode = 1;
     required string x509Certificate = 2; // Base64 encoded X509 certificate.
     optional string x509CACertificate = 3; // Base64 encoded CA X509 certificate.
+    // Base64 encoded Root CA X509 certificate.
+    optional string x509RootCACertificate = 4;
 }
 
 /**
@@ -169,6 +175,11 @@
     repeated string certificates = 2;
 }
 
+message SCMGetRootCACertificateRequestProto {
+}
+
+message SCMListCACertificateRequestProto {
+}
 
 service SCMSecurityProtocolService {
     rpc submitRequest (SCMSecurityRequest) returns (SCMSecurityResponse);
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SCMSecurityProtocolServerSideTranslatorPB.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SCMSecurityProtocolServerSideTranslatorPB.java
index 06da6e4..cc0c776 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SCMSecurityProtocolServerSideTranslatorPB.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SCMSecurityProtocolServerSideTranslatorPB.java
@@ -111,6 +111,12 @@
       case GetSCMCertificate:
         return scmSecurityResponse.setGetCertResponseProto(getSCMCertificate(
             request.getGetSCMCertificateRequest())).build();
+      case GetRootCACertificate:
+        return scmSecurityResponse.setGetCertResponseProto(
+            getRootCACertificate()).build();
+      case ListCACertificate:
+        return scmSecurityResponse.setListCertificateResponseProto(
+            listCACertificate()).build();
       default:
         throw new IllegalArgumentException(
             "Unknown request type: " + request.getCmdType());
@@ -257,4 +263,32 @@
 
 
   }
+
+
+  public SCMGetCertResponseProto getRootCACertificate() throws IOException {
+    String rootCACertificate = impl.getRootCACertificate();
+    SCMGetCertResponseProto.Builder builder =
+        SCMGetCertResponseProto
+            .newBuilder()
+            .setResponseCode(ResponseCode.success)
+            .setX509RootCACertificate(rootCACertificate);
+    return builder.build();
+  }
+
+  public SCMListCertificateResponseProto listCACertificate()
+      throws IOException {
+
+    List<String> certs = impl.listCACertificate();
+
+    SCMListCertificateResponseProto.Builder builder =
+        SCMListCertificateResponseProto
+            .newBuilder()
+            .setResponseCode(SCMListCertificateResponseProto
+                .ResponseCode.success)
+            .addAllCertificates(certs);
+    return builder.build();
+
+  }
+
+
 }
\ No newline at end of file
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
index 5df3aa7..5a1ecf3 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
@@ -57,6 +57,7 @@
 import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.CERTIFICATE_NOT_FOUND;
 import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.GET_CA_CERT_FAILED;
 import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.GET_CERTIFICATE_FAILED;
+import static org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.GET_ROOT_CA_CERT_FAILED;
 import static org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateApprover.ApprovalType.KERBEROS_TRUSTED;
 
 /**
@@ -283,6 +284,28 @@
     return results;
   }
 
+  @Override
+  public List<String> listCACertificate() throws IOException {
+    List<String> caCerts =
+        listCertificate(NodeType.SCM, 0, 10, false);
+    caCerts.add(getRootCACertificate());
+    return caCerts;
+  }
+
+  @Override
+  public String getRootCACertificate() throws IOException {
+    LOGGER.debug("Getting Root CA certificate.");
+    //TODO: This code will be modified after HDDS-4897 is merged and
+    // integrated. For now getting RootCA cert from certificateServer.
+    try {
+      return CertificateCodec.getPEMEncodedString(
+          certificateServer.getCACertificate());
+    } catch (CertificateException e) {
+      throw new SCMSecurityException("getRootCertificate operation failed. ",
+          e, GET_ROOT_CA_CERT_FAILED);
+    }
+  }
+
   public RPC.Server getRpcServer() {
     return rpcServer;
   }