mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-22 03:48:04 +01:00
Moving secret values out of the main configuration file
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.configuration.secrets;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.util.ExactlySize;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
public class SecretsTest {
|
||||
|
||||
private static final String SECRET_REF = "secret_string";
|
||||
|
||||
private static final String SECRET_LIST_REF = "secret_string_list";
|
||||
|
||||
private static final String SECRET_BYTES_REF = "secret_bytes";
|
||||
|
||||
private static final String SECRET_BYTES_LIST_REF = "secret_bytes_list";
|
||||
|
||||
public record TestData(SecretString secret,
|
||||
SecretBytes secretBytes,
|
||||
SecretStringList secretList,
|
||||
SecretBytesList secretBytesList) {
|
||||
}
|
||||
|
||||
private static final String VALID_CONFIG_YAML = """
|
||||
secret: secret://%s
|
||||
secretBytes: secret://%s
|
||||
secretList: secret://%s
|
||||
secretBytesList: secret://%s
|
||||
""".formatted(SECRET_REF, SECRET_BYTES_REF, SECRET_LIST_REF, SECRET_BYTES_LIST_REF);
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeserialization() throws Exception {
|
||||
final String secretString = "secret_string";
|
||||
final byte[] secretBytes = RandomUtils.nextBytes(16);
|
||||
final String secretBytesBase64 = Base64.getEncoder().encodeToString(secretBytes);
|
||||
final List<String> secretStringList = List.of("secret1", "secret2", "secret3");
|
||||
final List<byte[]> secretBytesList = List.of(RandomUtils.nextBytes(16), RandomUtils.nextBytes(16), RandomUtils.nextBytes(16));
|
||||
final List<String> secretBytesListBase64 = secretBytesList.stream().map(Base64.getEncoder()::encodeToString).toList();
|
||||
final Map<String, Secret<?>> storeMap = Map.of(
|
||||
SECRET_REF, new SecretString(secretString),
|
||||
SECRET_BYTES_REF, new SecretString(secretBytesBase64),
|
||||
SECRET_LIST_REF, new SecretStringList(secretStringList),
|
||||
SECRET_BYTES_LIST_REF, new SecretStringList(secretBytesListBase64)
|
||||
);
|
||||
SecretsModule.INSTANCE.setSecretStore(new SecretStore(storeMap));
|
||||
|
||||
final TestData result = SystemMapper.yamlMapper().readValue(VALID_CONFIG_YAML, TestData.class);
|
||||
assertEquals(secretString, result.secret().value());
|
||||
assertEquals(secretStringList, result.secretList().value());
|
||||
assertArrayEquals(secretBytes, result.secretBytes().value());
|
||||
for (int i = 0; i < secretBytesList.size(); i++) {
|
||||
assertArrayEquals(secretBytesList.get(i), result.secretBytesList().value().get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValueWithoutPrefix() throws Exception {
|
||||
final String config = """
|
||||
secret: ref
|
||||
""";
|
||||
SecretsModule.INSTANCE.setSecretStore(new SecretStore(Collections.emptyMap()));
|
||||
assertThrows(JsonMappingException.class, () -> SystemMapper.yamlMapper().readValue(config, TestData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoSecretInTheStore() throws Exception {
|
||||
final String config = """
|
||||
secret: secret://missing
|
||||
secretBytes: secret://missing
|
||||
secretList: secret://missing
|
||||
secretBytesList: secret://missing
|
||||
""";
|
||||
SecretsModule.INSTANCE.setSecretStore(new SecretStore(Collections.emptyMap()));
|
||||
assertThrows(JsonMappingException.class, () -> SystemMapper.yamlMapper().readValue(config, TestData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecretStoreNotSet() throws Exception {
|
||||
assertThrows(JsonMappingException.class, () -> SystemMapper.yamlMapper().readValue(VALID_CONFIG_YAML, TestData.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFromJson() throws Exception {
|
||||
// checking that valid json secrets bundle is read correctly
|
||||
final SecretStore secretStore = SecretStore.fromYamlStringSecretsBundle("""
|
||||
secret_string: value
|
||||
secret_string_list:
|
||||
- value1
|
||||
- value2
|
||||
- value3
|
||||
""");
|
||||
assertEquals("value", secretStore.secretString("secret_string").value());
|
||||
assertEquals(List.of("value1", "value2", "value3"), secretStore.secretStringList("secret_string_list").value());
|
||||
|
||||
// checking that secrets bundle can't have objects as values
|
||||
assertThrows(IllegalArgumentException.class, () -> SecretStore.fromYamlStringSecretsBundle("""
|
||||
secret_string: value
|
||||
not_a_string_or_list:
|
||||
k: v
|
||||
"""));
|
||||
|
||||
// checking that secrets bundle can't have numbers as values
|
||||
assertThrows(IllegalArgumentException.class, () -> SecretStore.fromYamlStringSecretsBundle("""
|
||||
secret_string: value
|
||||
not_a_string_or_list: 42
|
||||
"""));
|
||||
}
|
||||
|
||||
record NotEmptySecretStringList(@NotEmpty SecretStringList secret) {
|
||||
}
|
||||
|
||||
record NotEmptySecretBytesList(@NotEmpty SecretBytesList secret) {
|
||||
}
|
||||
|
||||
record ExactlySizeBytesSecret(@ExactlySize(32) SecretBytes secret) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidators() throws Exception {
|
||||
final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
|
||||
// @NotEmpty SecretStringList
|
||||
assertFalse(validator.validate(new NotEmptySecretStringList(new SecretStringList(List.of()))).isEmpty());
|
||||
assertTrue(validator.validate(new NotEmptySecretStringList(new SecretStringList(List.of("smth")))).isEmpty());
|
||||
|
||||
// @NotEmpty SecretBytesList
|
||||
assertFalse(validator.validate(new NotEmptySecretBytesList(new SecretBytesList(List.of()))).isEmpty());
|
||||
assertTrue(validator.validate(new NotEmptySecretBytesList(new SecretBytesList(List.of(new byte[4])))).isEmpty());
|
||||
|
||||
// @ExactlySize SecretBytes
|
||||
assertFalse(validator.validate(new ExactlySizeBytesSecret(new SecretBytes(new byte[16]))).isEmpty());
|
||||
assertTrue(validator.validate(new ExactlySizeBytesSecret(new SecretBytes(new byte[32]))).isEmpty());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user