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

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


The following commit(s) were added to refs/heads/master by this push:
     new 51d3aef  RATIS-1539. Refactor GrpcTlsConfig. (#613)
51d3aef is described below

commit 51d3aefaf72560cf0e8b57d721139bc2f2db8fe1
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Tue Mar 1 22:58:16 2022 +0800

    RATIS-1539. Refactor GrpcTlsConfig. (#613)
---
 .../java/org/apache/ratis/security/TlsConf.java    | 218 +++++++++++++++++++++
 .../java/org/apache/ratis/grpc/GrpcTlsConfig.java  |  91 +++++----
 2 files changed, 274 insertions(+), 35 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/security/TlsConf.java 
b/ratis-common/src/main/java/org/apache/ratis/security/TlsConf.java
new file mode 100644
index 0000000..e012d57
--- /dev/null
+++ b/ratis-common/src/main/java/org/apache/ratis/security/TlsConf.java
@@ -0,0 +1,218 @@
+/*
+ * 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.ratis.security;
+
+import org.apache.ratis.util.JavaUtils;
+import org.apache.ratis.util.Preconditions;
+
+import java.io.File;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * TLS configurations.
+ */
+public class TlsConf {
+  /**
+   * The value is either an actual object or a file containing the object.
+   * @param <V> The value type.
+   */
+  private static class FileBasedValue<V> {
+    private final V value;
+    private final File file;
+
+    FileBasedValue(V value) {
+      this.value = Objects.requireNonNull(value, () -> "value == null in " + 
getClass());
+      this.file = null;
+
+      if (value instanceof Iterable) {
+        final Iterator<?> i = ((Iterable<?>) value).iterator();
+        Preconditions.assertTrue(i.hasNext(), "value is an empty Iterable in " 
+ getClass());
+        Objects.requireNonNull(i.next(), () -> "The first item in value is 
null in " + getClass());
+      }
+    }
+
+    FileBasedValue(File file) {
+      this.value = null;
+      this.file = Objects.requireNonNull(file, () -> "file == null in " + 
getClass());
+    }
+
+    public V get() {
+      return value;
+    }
+
+    public File getFile() {
+      return file;
+    }
+
+    public final boolean isFileBased() {
+      return getFile() != null;
+    }
+  }
+
+  /** Configuration for {@link X509Certificate}s. */
+  public static class CertificatesConf extends 
FileBasedValue<Iterable<X509Certificate>> {
+    public CertificatesConf(Iterable<X509Certificate> certificates) {
+      super(certificates);
+    }
+    public CertificatesConf(X509Certificate... certificates) {
+      this(Arrays.asList(certificates));
+    }
+    public CertificatesConf(File certificates) {
+      super(certificates);
+    }
+  }
+
+  /** Configuration for a {@link PrivateKey}. */
+  public static class PrivateKeyConf extends FileBasedValue<PrivateKey> {
+    public PrivateKeyConf(PrivateKey privateKey) {
+      super(privateKey);
+    }
+    public PrivateKeyConf(File privateKeyFile) {
+      super(privateKeyFile);
+    }
+  }
+
+  /** Configurations for a trust manager. */
+  public static final class TrustManagerConf {
+    /** Trust certificates. */
+    private final CertificatesConf trustCertificates;
+
+    private TrustManagerConf(CertificatesConf trustCertificates) {
+      this.trustCertificates = trustCertificates;
+    }
+
+    /** @return the trust certificates. */
+    public CertificatesConf getTrustCertificates() {
+      return trustCertificates;
+    }
+  }
+
+  /** Configurations for a key manager. */
+  public static final class KeyManagerConf {
+    /** A {@link PrivateKey}. */
+    private final PrivateKeyConf privateKey;
+    /** Certificates for the private key. */
+    private final CertificatesConf keyCertificates;
+
+    private KeyManagerConf(PrivateKeyConf privateKey, CertificatesConf 
keyCertificates) {
+      this.privateKey = Objects.requireNonNull(privateKey, "privateKey == 
null");
+      this.keyCertificates = Objects.requireNonNull(keyCertificates, 
"keyCertificates == null");
+    }
+
+    /** @return the private key. */
+    public PrivateKeyConf getPrivateKey() {
+      return privateKey;
+    }
+
+    /** @return the certificates for the private key. */
+    public CertificatesConf getKeyCertificates() {
+      return keyCertificates;
+    }
+  }
+
+  private static final AtomicInteger COUNT = new AtomicInteger();
+
+  private final String name;
+  private final KeyManagerConf keyManager;
+  private final TrustManagerConf trustManager;
+
+  private TlsConf(String name, KeyManagerConf keyManager, TrustManagerConf 
trustManager) {
+    this.name = JavaUtils.getClassSimpleName(getClass()) + 
COUNT.getAndIncrement() + (name == null? "": "-" + name);
+    this.keyManager = keyManager;
+    this.trustManager = trustManager;
+  }
+
+  protected TlsConf(Builder b) {
+    this(b.buildName(), b.buildKeyManagerConf(), b.buildTrustManagerConf());
+  }
+
+  /** @return the key manager configuration. */
+  public KeyManagerConf getKeyManager() {
+    return keyManager;
+  }
+
+  /** @return the trust manager configuration. */
+  public TrustManagerConf getTrustManager() {
+    return trustManager;
+  }
+
+  @Override
+  public String toString() {
+    return name;
+  }
+
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  /** For building {@link TlsConf}. */
+  public static class Builder {
+    private String name;
+    private CertificatesConf trustCertificates;
+    private PrivateKeyConf privateKey;
+    private CertificatesConf keyCertificates;
+
+    public Builder setName(String name) {
+      this.name = name;
+      return this;
+    }
+
+    public Builder setTrustCertificates(CertificatesConf trustCertificates) {
+      this.trustCertificates = trustCertificates;
+      return this;
+    }
+
+    public Builder setPrivateKey(PrivateKeyConf privateKey) {
+      this.privateKey = privateKey;
+      return this;
+    }
+
+    public Builder setKeyCertificates(CertificatesConf keyCertificates) {
+      this.keyCertificates = keyCertificates;
+      return this;
+    }
+
+    private String buildName() {
+      return Optional.ofNullable(name).orElse("");
+    }
+
+    private TrustManagerConf buildTrustManagerConf() {
+      return new TrustManagerConf(trustCertificates);
+    }
+
+    private KeyManagerConf buildKeyManagerConf() {
+      if (privateKey == null && keyCertificates == null) {
+        return null;
+      } else if (privateKey != null && keyCertificates != null) {
+        return new KeyManagerConf(privateKey, keyCertificates);
+      }
+      throw new IllegalStateException("Must be either all null or all 
non-null: privateKey == null? "
+          + (privateKey == null) + ", keyCertificates == null? " + 
(keyCertificates == null));
+    }
+
+    public TlsConf build() {
+      return new TlsConf(this);
+    }
+  }
+}
\ No newline at end of file
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java 
b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java
index 3a17145..39aef11 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcTlsConfig.java
@@ -17,31 +17,21 @@
  */
 package org.apache.ratis.grpc;
 
+import org.apache.ratis.security.TlsConf;
+
 import java.io.File;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Ratis GRPC TLS configurations.
  */
-public class GrpcTlsConfig {
-  // private key
-  private PrivateKey privateKey;
-  private File privateKeyFile;
-
-  // certificate
-  private X509Certificate certChain;
-  private File certChainFile;
-
-  // ca certificate
-  private List<X509Certificate> trustStore;
-  private File trustStoreFile;
-
-  // mutual TLS enabled
+public class GrpcTlsConfig extends TlsConf {
   private final boolean mTlsEnabled;
-
   private final boolean fileBasedConfig;
 
   public boolean isFileBasedConfig() {
@@ -49,27 +39,47 @@ public class GrpcTlsConfig {
   }
 
   public PrivateKey getPrivateKey() {
-    return privateKey;
+    return Optional.ofNullable(getKeyManager())
+        .map(KeyManagerConf::getPrivateKey)
+        .map(PrivateKeyConf::get)
+        .orElse(null);
   }
 
   public File getPrivateKeyFile() {
-    return privateKeyFile;
+    return Optional.ofNullable(getKeyManager())
+        .map(KeyManagerConf::getPrivateKey)
+        .map(PrivateKeyConf::getFile)
+        .orElse(null);
   }
 
   public X509Certificate getCertChain() {
-    return certChain;
+    return Optional.ofNullable(getKeyManager())
+        .map(KeyManagerConf::getKeyCertificates)
+        .map(CertificatesConf::get)
+        .map(Iterable::iterator)
+        .map(Iterator::next)
+        .orElse(null);
   }
 
   public File getCertChainFile() {
-    return certChainFile;
+    return Optional.ofNullable(getKeyManager())
+        .map(KeyManagerConf::getKeyCertificates)
+        .map(CertificatesConf::getFile)
+        .orElse(null);
   }
 
   public List<X509Certificate> getTrustStore() {
-    return trustStore;
+    return (List<X509Certificate>) Optional.ofNullable(getTrustManager())
+        .map(TrustManagerConf::getTrustCertificates)
+        .map(CertificatesConf::get)
+        .orElse(null);
   }
 
   public File getTrustStoreFile() {
-    return trustStoreFile;
+    return Optional.ofNullable(getTrustManager())
+        .map(TrustManagerConf::getTrustCertificates)
+        .map(CertificatesConf::getFile)
+        .orElse(null);
   }
 
   public boolean getMtlsEnabled() {
@@ -78,28 +88,39 @@ public class GrpcTlsConfig {
 
   public GrpcTlsConfig(PrivateKey privateKey, X509Certificate certChain,
       List<X509Certificate> trustStore, boolean mTlsEnabled) {
-    this.privateKey = privateKey;
-    this.certChain = certChain;
-    this.trustStore = trustStore;
-    this.mTlsEnabled = mTlsEnabled;
-    this.fileBasedConfig = false;
+    this(newBuilder(privateKey, certChain, trustStore), mTlsEnabled, false);
   }
 
   public GrpcTlsConfig(PrivateKey privateKey, X509Certificate certChain,
       X509Certificate trustStore, boolean mTlsEnabled) {
-    this.privateKey = privateKey;
-    this.certChain = certChain;
-    this.trustStore = Collections.singletonList(trustStore);
-    this.mTlsEnabled = mTlsEnabled;
-    this.fileBasedConfig = false;
+    this(privateKey, certChain, Collections.singletonList(trustStore), 
mTlsEnabled);
   }
 
   public GrpcTlsConfig(File privateKeyFile, File certChainFile,
       File trustStoreFile, boolean mTlsEnabled) {
-    this.privateKeyFile = privateKeyFile;
-    this.certChainFile = certChainFile;
-    this.trustStoreFile = trustStoreFile;
+    this(newBuilder(privateKeyFile, certChainFile, trustStoreFile), 
mTlsEnabled, true);
+  }
+
+  private GrpcTlsConfig(Builder builder, boolean mTlsEnabled, boolean 
fileBasedConfig) {
+    super(builder);
     this.mTlsEnabled = mTlsEnabled;
-    this.fileBasedConfig = true;
+    this.fileBasedConfig = fileBasedConfig;
+  }
+
+  private static Builder newBuilder(PrivateKey privateKey, X509Certificate 
certChain,
+      List<X509Certificate> trustStore) {
+    final Builder b = newBuilder();
+    
Optional.ofNullable(trustStore).map(CertificatesConf::new).ifPresent(b::setTrustCertificates);
+    
Optional.ofNullable(privateKey).map(PrivateKeyConf::new).ifPresent(b::setPrivateKey);
+    
Optional.ofNullable(certChain).map(CertificatesConf::new).ifPresent(b::setKeyCertificates);
+    return b;
+  }
+
+  private static Builder newBuilder(File privateKeyFile, File certChainFile, 
File trustStoreFile) {
+    final Builder b = newBuilder();
+    
Optional.ofNullable(trustStoreFile).map(CertificatesConf::new).ifPresent(b::setTrustCertificates);
+    
Optional.ofNullable(privateKeyFile).map(PrivateKeyConf::new).ifPresent(b::setPrivateKey);
+    
Optional.ofNullable(certChainFile).map(CertificatesConf::new).ifPresent(b::setKeyCertificates);
+    return b;
   }
 }
\ No newline at end of file

Reply via email to