1818
1919import java .nio .file .Path ;
2020import java .time .Duration ;
21+ import java .util .HashMap ;
22+ import java .util .List ;
2123import java .util .Map ;
2224import java .util .Optional ;
2325
@@ -68,10 +70,6 @@ public class RemoteStorageManagerConfig extends AbstractConfig {
6870
6971 private static final String ENCRYPTION_CONFIG = "encryption.enabled" ;
7072 private static final String ENCRYPTION_DOC = "Whether to enable encryption" ;
71- private static final String ENCRYPTION_PUBLIC_KEY_FILE_CONFIG = "encryption.public.key.file" ;
72- private static final String ENCRYPTION_PUBLIC_KEY_FILE_DOC = "The path to the RSA public key file" ;
73- private static final String ENCRYPTION_PRIVATE_KEY_FILE_CONFIG = "encryption.private.key.file" ;
74- private static final String ENCRYPTION_PRIVATE_KEY_FILE_DOC = "The path to the RSA private key file" ;
7573 // TODO add possibility to pass keys as strings
7674
7775
@@ -158,20 +156,6 @@ public class RemoteStorageManagerConfig extends AbstractConfig {
158156 ConfigDef .Importance .HIGH ,
159157 ENCRYPTION_DOC
160158 );
161- CONFIG .define (
162- ENCRYPTION_PUBLIC_KEY_FILE_CONFIG ,
163- ConfigDef .Type .STRING ,
164- null ,
165- ConfigDef .Importance .HIGH ,
166- ENCRYPTION_PUBLIC_KEY_FILE_DOC
167- );
168- CONFIG .define (
169- ENCRYPTION_PRIVATE_KEY_FILE_CONFIG ,
170- ConfigDef .Type .STRING ,
171- null ,
172- ConfigDef .Importance .HIGH ,
173- ENCRYPTION_PRIVATE_KEY_FILE_DOC
174- );
175159
176160 CONFIG
177161 .define (METRICS_SAMPLE_WINDOW_MS_CONFIG ,
@@ -196,14 +180,107 @@ public class RemoteStorageManagerConfig extends AbstractConfig {
196180 METRICS_RECORDING_LEVEL_DOC );
197181 }
198182
183+ /**
184+ * Internal config for encryption.
185+ *
186+ * <p>It's needed for more convenient dynamic config definition.
187+ */
188+ private static class EncryptionConfig extends AbstractConfig {
189+ private static final String ENCRYPTION_KEY_PAIR_ID_CONFIG = "encryption.key.pair.id" ;
190+ private static final String ENCRYPTION_KEY_PAIR_ID_DOC =
191+ "The ID of the key pair to be used for encryption" ;
192+
193+ private static final String ENCRYPTION_KEY_PAIRS_CONFIG = "encryption.key.pairs" ;
194+ private static final String ENCRYPTION_KEY_PAIRS_DOC = "The list of encryption key pair IDs" ;
195+
196+ private static final String ENCRYPTION_PUBLIC_KEY_FILE_DOC = "The path to the RSA public key file" ;
197+ private static final String ENCRYPTION_PRIVATE_KEY_FILE_DOC = "The path to the RSA private key file" ;
198+
199+ private EncryptionConfig (final ConfigDef configDef , final Map <String , ?> props ) {
200+ super (configDef , props );
201+ }
202+
203+ Path encryptionPublicKeyFile (final String keyPairId ) {
204+ return Path .of (getString (publicKeyFileConfig (keyPairId )));
205+ }
206+
207+ Path encryptionPrivateKeyFile (final String keyPairId ) {
208+ return Path .of (getString (privateKeyFileConfig (keyPairId )));
209+ }
210+
211+ public static EncryptionConfig create (final Map <String , ?> props ) {
212+ final ConfigDef configDef = new ConfigDef ();
213+ // First, define the active key ID and key ID list fields, they are required always.
214+ configDef .define (
215+ ENCRYPTION_KEY_PAIR_ID_CONFIG ,
216+ ConfigDef .Type .STRING ,
217+ ConfigDef .NO_DEFAULT_VALUE ,
218+ ConfigDef .Importance .HIGH ,
219+ ENCRYPTION_KEY_PAIR_ID_DOC
220+ );
221+ configDef .define (
222+ ENCRYPTION_KEY_PAIRS_CONFIG ,
223+ ConfigDef .Type .LIST ,
224+ ConfigDef .NO_DEFAULT_VALUE ,
225+ ConfigDef .Importance .HIGH ,
226+ ENCRYPTION_KEY_PAIRS_DOC
227+ );
228+ final EncryptionConfig interimEncryptionConfig = new EncryptionConfig (configDef , props );
229+
230+ // Check that the active ID is present in the list.
231+ if (!interimEncryptionConfig .keyPairIds ().contains (interimEncryptionConfig .activeKeyPairId ())) {
232+ throw new ConfigException (
233+ "Encryption key '" + interimEncryptionConfig .activeKeyPairId () + "' must be provided" );
234+ }
235+
236+ // Then, define key fields dynamically based on the key pair IDs provided above.
237+ // See e.g. the ConnectorConfig.enrich in the Kafka code.
238+ for (final String keyPairId : interimEncryptionConfig .keyPairIds ()) {
239+ configDef .define (
240+ publicKeyFileConfig (keyPairId ),
241+ ConfigDef .Type .STRING ,
242+ ConfigDef .NO_DEFAULT_VALUE ,
243+ ConfigDef .Importance .HIGH ,
244+ ENCRYPTION_PUBLIC_KEY_FILE_DOC
245+ );
246+ configDef .define (
247+ privateKeyFileConfig (keyPairId ),
248+ ConfigDef .Type .STRING ,
249+ ConfigDef .NO_DEFAULT_VALUE ,
250+ ConfigDef .Importance .HIGH ,
251+ ENCRYPTION_PRIVATE_KEY_FILE_DOC
252+ );
253+ }
254+
255+ return new EncryptionConfig (configDef , props );
256+ }
257+
258+ String activeKeyPairId () {
259+ return getString (ENCRYPTION_KEY_PAIR_ID_CONFIG );
260+ }
261+
262+ List <String > keyPairIds () {
263+ return getList (ENCRYPTION_KEY_PAIRS_CONFIG );
264+ }
265+
266+ private static String publicKeyFileConfig (final String keyPairId ) {
267+ return "encryption.key.pairs." + keyPairId + ".public.key.file" ;
268+ }
269+
270+ private static String privateKeyFileConfig (final String keyPairId ) {
271+ return "encryption.key.pairs." + keyPairId + ".private.key.file" ;
272+ }
273+ }
274+
275+ private final EncryptionConfig encryptionConfig ;
199276
200277 RemoteStorageManagerConfig (final Map <String , ?> props ) {
201278 super (CONFIG , props );
279+ encryptionConfig = encryptionEnabled () ? EncryptionConfig .create (props ) : null ;
202280 validate ();
203281 }
204282
205283 private void validate () {
206- validateEncryption ();
207284 validateCompression ();
208285 }
209286
@@ -214,17 +291,6 @@ private void validateCompression() {
214291 }
215292 }
216293
217- private void validateEncryption () {
218- if (getBoolean (ENCRYPTION_CONFIG ) && getString (ENCRYPTION_PUBLIC_KEY_FILE_CONFIG ) == null ) {
219- throw new ConfigException (
220- ENCRYPTION_PUBLIC_KEY_FILE_CONFIG + " must be provided if encryption is enabled" );
221- }
222- if (getBoolean (ENCRYPTION_CONFIG ) && getString (ENCRYPTION_PRIVATE_KEY_FILE_CONFIG ) == null ) {
223- throw new ConfigException (
224- ENCRYPTION_PRIVATE_KEY_FILE_CONFIG + " must be provided if encryption is enabled" );
225- }
226- }
227-
228294 StorageBackend storage () {
229295 final Class <?> storageClass = getClass (STORAGE_BACKEND_CLASS_CONFIG );
230296 final StorageBackend storage = Utils .newInstance (storageClass , StorageBackend .class );
@@ -268,19 +334,25 @@ boolean encryptionEnabled() {
268334 return getBoolean (ENCRYPTION_CONFIG );
269335 }
270336
271- Path encryptionPublicKeyFile () {
272- final String value = getString (ENCRYPTION_PUBLIC_KEY_FILE_CONFIG );
273- if (value == null ) {
337+ String encryptionKeyPairId () {
338+ if (!encryptionEnabled ()) {
274339 return null ;
275340 }
276- return Path . of ( value );
341+ return encryptionConfig . activeKeyPairId ( );
277342 }
278343
279- Path encryptionPrivateKeyFile () {
280- final String value = getString (ENCRYPTION_PRIVATE_KEY_FILE_CONFIG );
281- if (value == null ) {
344+ Map <String , KeyPairPaths > encryptionKeyRing () {
345+ if (!encryptionEnabled ()) {
282346 return null ;
283347 }
284- return Path .of (value );
348+
349+ final Map <String , KeyPairPaths > result = new HashMap <>();
350+ for (final String keyPairId : encryptionConfig .keyPairIds ()) {
351+ final KeyPairPaths keyPair = new KeyPairPaths (
352+ encryptionConfig .encryptionPublicKeyFile (keyPairId ),
353+ encryptionConfig .encryptionPrivateKeyFile (keyPairId ));
354+ result .put (keyPairId , keyPair );
355+ }
356+ return result ;
285357 }
286358}
0 commit comments