1   /*
2    * Copyright 2018 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package com.linecorp.centraldogma.server;
17  
18  import static com.google.common.base.Preconditions.checkArgument;
19  import static com.linecorp.centraldogma.server.CentralDogmaConfig.convertValue;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.nio.file.Files;
26  
27  import javax.annotation.Nullable;
28  
29  import com.fasterxml.jackson.annotation.JsonCreator;
30  import com.fasterxml.jackson.annotation.JsonProperty;
31  import com.google.common.base.MoreObjects;
32  import com.google.common.io.ByteStreams;
33  import com.google.errorprone.annotations.MustBeClosed;
34  
35  /**
36   * TLS configuration.
37   */
38  public final class TlsConfig {
39  
40      @Nullable
41      private final File keyCertChainFile;
42      @Nullable
43      private final File keyFile;
44      @Nullable
45      private final String keyCertChain;
46      @Nullable
47      private final String key;
48      @Nullable
49      private final String keyPassword;
50  
51      /**
52       * Creates an instance with the specified {@code keyCertChainFilePath}, {@code keyFilePath} and
53       * {@code keyPassword}.
54       */
55      @JsonCreator
56      public TlsConfig(@JsonProperty("keyCertChainFile") @Nullable File keyCertChainFile,
57                       @JsonProperty("keyFile") @Nullable File keyFile,
58                       @JsonProperty("keyCertChain") @Nullable String keyCertChain,
59                       @JsonProperty("key") @Nullable String key,
60                       @JsonProperty("keyPassword") @Nullable String keyPassword) {
61          validate(keyCertChainFile, keyCertChain, "keyCertChainFile", "keyCertChain");
62          validate(keyFile, key, "keyFile", "key");
63  
64          this.keyCertChainFile = keyCertChainFile;
65          this.keyFile = keyFile;
66          // keyCertChain and key are converted later when it's used.
67          this.keyCertChain = keyCertChain;
68          this.key = key;
69          this.keyPassword = keyPassword;
70      }
71  
72      private static void validate(@Nullable File fileName, @Nullable String name,
73                                   String first, String second) {
74          checkArgument(fileName != null || name != null,
75                        "%s and %s cannot be null at the same time.", first, second);
76          if (fileName != null && name != null) {
77              throw new IllegalArgumentException(
78                      String.format("%s and %s cannot be specified at the same time.", first, second));
79          }
80      }
81  
82      /**
83       * Returns a certificates file which is created with the given {@code keyCertChainFilePath}.
84       *
85       * @deprecated Use {@link #keyCertChainInputStream()}.
86       */
87      @Nullable
88      @JsonProperty
89      @Deprecated
90      public File keyCertChainFile() {
91          return keyCertChainFile;
92      }
93  
94      /**
95       * Returns a private key file which is created with the given {@code keyFilePath}.
96       *
97       * @deprecated Use {@link #keyInputStream()}.
98       */
99      @Nullable
100     @JsonProperty
101     @Deprecated
102     public File keyFile() {
103         return keyFile;
104     }
105 
106     /**
107      * Returns an {@link InputStream} of the certificate chain.
108      */
109     @MustBeClosed
110     public InputStream keyCertChainInputStream() {
111         return inputStream(keyCertChainFile, keyCertChain, "keyCertChain");
112     }
113 
114     /**
115      * Returns an {@link InputStream} of the private key.
116      */
117     @MustBeClosed
118     public InputStream keyInputStream() {
119         return inputStream(keyFile, key, "key");
120     }
121 
122     private static InputStream inputStream(@Nullable File file,
123                                            @Nullable String property, String propertyName) {
124         if (file != null) {
125             try (InputStream inputStream = Files.newInputStream(file.toPath())) {
126                 // Use byte array to avoid file descriptor leak.
127                 return new ByteArrayInputStream(ByteStreams.toByteArray(inputStream));
128             } catch (IOException e) {
129                 throw new RuntimeException("failed to create an input stream from " + file, e);
130             }
131         }
132 
133         assert property != null;
134         final String converted = convertValue(property, "tls." + propertyName);
135         if (converted == null) {
136             throw new NullPointerException(propertyName + '(' + property + ") is converted to null.");
137         }
138         return new ByteArrayInputStream(converted.getBytes());
139     }
140 
141     /**
142      * Returns a password for the private key file. Return {@code null} if no password is set.
143      */
144     @JsonProperty
145     @Nullable
146     public String keyPassword() {
147         return convertValue(keyPassword, "tls.keyPassword");
148     }
149 
150     @Override
151     public String toString() {
152         return MoreObjects.toStringHelper(this).omitNullValues()
153                           .add("keyCertChainFile", keyCertChainFile)
154                           .add("keyFile", keyFile)
155                           .toString();
156     }
157 }