Skip to content
Snippets Groups Projects
Commit 9e35e7c2 authored by Jan Zickermann's avatar Jan Zickermann
Browse files

OZG-4097 Delay validation of api client configuration

parent 141f040f
No related branches found
Tags
No related merge requests found
package de.ozgcloud.nachrichten.postfach.osiv2.config; package de.ozgcloud.nachrichten.postfach.osiv2.config;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
...@@ -28,11 +36,15 @@ import org.springframework.util.LinkedMultiValueMap; ...@@ -28,11 +36,15 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.ozgcloud.nachrichten.postfach.osiv2.gen.ApiClient; import de.ozgcloud.nachrichten.postfach.osiv2.gen.ApiClient;
import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.MessageExchangeApi;
import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.QuarantineApi; import de.ozgcloud.nachrichten.postfach.osiv2.gen.api.QuarantineApi;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
@Log4j2
@Configuration @Configuration
@RequiredArgsConstructor @RequiredArgsConstructor
@ConditionalOnProperty(prefix = Osi2PostfachProperties.PREFIX, name = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = Osi2PostfachProperties.PREFIX, name = "enabled", havingValue = "true")
...@@ -42,6 +54,8 @@ public class ApiClientConfiguration { ...@@ -42,6 +54,8 @@ public class ApiClientConfiguration {
private final Osi2PostfachProperties.ApiConfiguration apiConfiguration; private final Osi2PostfachProperties.ApiConfiguration apiConfiguration;
private final Osi2PostfachProperties.ProxyConfiguration proxyConfiguration; private final Osi2PostfachProperties.ProxyConfiguration proxyConfiguration;
private final Validator validator;
private static final String CLIENT_REGISTRATION_ID = "osi2"; private static final String CLIENT_REGISTRATION_ID = "osi2";
@Bean @Bean
...@@ -55,8 +69,11 @@ public class ApiClientConfiguration { ...@@ -55,8 +69,11 @@ public class ApiClientConfiguration {
} }
@Bean @Bean
@SneakyThrows
ApiClient apiClient() { ApiClient apiClient() {
getErrorMessageOfViolations();
var apiClient = new ApiClient(restClient()); var apiClient = new ApiClient(restClient());
LOG.debug("Setting api client base path to {}", apiConfiguration.getUrl());
apiClient.setBasePath(apiConfiguration.getUrl()); apiClient.setBasePath(apiConfiguration.getUrl());
return apiClient; return apiClient;
} }
...@@ -78,6 +95,7 @@ public class ApiClientConfiguration { ...@@ -78,6 +95,7 @@ public class ApiClientConfiguration {
private ClientHttpRequestFactory createProxyRequestFactory() { private ClientHttpRequestFactory createProxyRequestFactory() {
var requestFactory = new HttpComponentsClientHttpRequestFactory(); var requestFactory = new HttpComponentsClientHttpRequestFactory();
if (proxyConfiguration.isEnabled()) { if (proxyConfiguration.isEnabled()) {
LOG.debug("Using proxy configuration: {}:{}", proxyConfiguration.getHost(), proxyConfiguration.getPort());
requestFactory.setHttpClient( requestFactory.setHttpClient(
HttpClientBuilder.create() HttpClientBuilder.create()
.setProxy(new HttpHost(proxyConfiguration.getHost(), proxyConfiguration.getPort())) .setProxy(new HttpHost(proxyConfiguration.getHost(), proxyConfiguration.getPort()))
...@@ -93,6 +111,7 @@ public class ApiClientConfiguration { ...@@ -93,6 +111,7 @@ public class ApiClientConfiguration {
var username = proxyConfiguration.getUsername(); var username = proxyConfiguration.getUsername();
var password = proxyConfiguration.getPassword(); var password = proxyConfiguration.getPassword();
if (username != null && password != null) { if (username != null && password != null) {
LOG.debug("Using proxy authentication with username {}", username);
credentialsProvider.setCredentials(new AuthScope(proxyConfiguration.getHost(), proxyConfiguration.getPort()), credentialsProvider.setCredentials(new AuthScope(proxyConfiguration.getHost(), proxyConfiguration.getPort()),
new UsernamePasswordCredentials(username, password.toCharArray())); new UsernamePasswordCredentials(username, password.toCharArray()));
} }
...@@ -116,6 +135,8 @@ public class ApiClientConfiguration { ...@@ -116,6 +135,8 @@ public class ApiClientConfiguration {
} }
private ClientRegistration osi2ClientRegistration() { private ClientRegistration osi2ClientRegistration() {
LOG.debug("Creating client registration with clientId: {}, tokenUri: {}, scope: {}, resource: {}", authConfiguration.getClientId(),
authConfiguration.getTokenUri(), authConfiguration.getScope(), authConfiguration.getResource());
return ClientRegistration.withRegistrationId(CLIENT_REGISTRATION_ID) return ClientRegistration.withRegistrationId(CLIENT_REGISTRATION_ID)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
...@@ -161,4 +182,28 @@ public class ApiClientConfiguration { ...@@ -161,4 +182,28 @@ public class ApiClientConfiguration {
}); });
} }
void getErrorMessageOfViolations() {
String violationMessage = Stream.<Optional<String>>of(getErrorMessageOfViolations(authConfiguration),
getErrorMessageOfViolations(apiConfiguration),
proxyConfiguration.isEnabled() ? getErrorMessageOfViolations(proxyConfiguration) : Optional.empty()
).flatMap(Optional::stream)
.collect(Collectors.joining(System.lineSeparator()));
if (!violationMessage.isEmpty()) {
throw new TechnicalException(
"Configuration of api client is invalid:%s%s".formatted(System.lineSeparator(), violationMessage));
}
}
private <T> Optional<String> getErrorMessageOfViolations(T configuration) {
return formatConstraintValidation(validator.validate(configuration))
.map(message -> "%s is invalid: %s".formatted(configuration.getClass().getSimpleName(), message));
}
private <T> Optional<String> formatConstraintValidation(Set<ConstraintViolation<T>> constraints) {
return constraints.stream()
.map(violation -> String.format("%s: %s", violation.getPropertyPath(), violation.getMessage()))
.reduce((a, b) -> a + ", " + b);
}
} }
...@@ -8,7 +8,6 @@ import jakarta.validation.constraints.NotBlank; ...@@ -8,7 +8,6 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import lombok.Getter; import lombok.Getter;
...@@ -18,7 +17,6 @@ import lombok.Setter; ...@@ -18,7 +17,6 @@ import lombok.Setter;
@Setter @Setter
@Configuration @Configuration
@ConfigurationProperties(prefix = Osi2PostfachProperties.PREFIX) @ConfigurationProperties(prefix = Osi2PostfachProperties.PREFIX)
@ConfigurationPropertiesScan
public class Osi2PostfachProperties { public class Osi2PostfachProperties {
// From de.ozgcloud.nachrichten.NachrichtenManagerConfiguration // From de.ozgcloud.nachrichten.NachrichtenManagerConfiguration
public static final String GRPC_FILE_MANAGER_NAME = "file-manager"; public static final String GRPC_FILE_MANAGER_NAME = "file-manager";
...@@ -29,12 +27,10 @@ public class Osi2PostfachProperties { ...@@ -29,12 +27,10 @@ public class Osi2PostfachProperties {
private boolean enabled; private boolean enabled;
@Getter @Getter
@Setter @Setter
@Configuration @Configuration
@ConfigurationProperties(prefix = AuthConfiguration.PREFIX) @ConfigurationProperties(prefix = AuthConfiguration.PREFIX)
@ConfigurationPropertiesScan
public static class AuthConfiguration { public static class AuthConfiguration {
public static final String PREFIX = Osi2PostfachProperties.PREFIX + ".auth"; public static final String PREFIX = Osi2PostfachProperties.PREFIX + ".auth";
...@@ -55,7 +51,6 @@ public class Osi2PostfachProperties { ...@@ -55,7 +51,6 @@ public class Osi2PostfachProperties {
@Setter @Setter
@Configuration @Configuration
@ConfigurationProperties(prefix = ApiConfiguration.PREFIX) @ConfigurationProperties(prefix = ApiConfiguration.PREFIX)
@ConfigurationPropertiesScan
public static class ApiConfiguration { public static class ApiConfiguration {
public static final String PREFIX = Osi2PostfachProperties.PREFIX + ".api"; public static final String PREFIX = Osi2PostfachProperties.PREFIX + ".api";
...@@ -75,7 +70,6 @@ public class Osi2PostfachProperties { ...@@ -75,7 +70,6 @@ public class Osi2PostfachProperties {
@Setter @Setter
@Configuration @Configuration
@ConfigurationProperties(prefix = ProxyConfiguration.PREFIX) @ConfigurationProperties(prefix = ProxyConfiguration.PREFIX)
@ConfigurationPropertiesScan
public static class ProxyConfiguration { public static class ProxyConfiguration {
public static final String PREFIX = Osi2PostfachProperties.PREFIX + ".proxy"; public static final String PREFIX = Osi2PostfachProperties.PREFIX + ".proxy";
......
package de.ozgcloud.nachrichten.postfach.osiv2.config;
import java.util.Set;
import javax.annotation.PostConstruct;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.ozgcloud.nachrichten.postfach.osiv2.ServiceIfOsi2Enabled;
import lombok.RequiredArgsConstructor;
@ServiceIfOsi2Enabled
@RequiredArgsConstructor
public class Osi2PropertiesValidator {
private final Osi2PostfachProperties.AuthConfiguration authConfiguration;
private final Osi2PostfachProperties.ApiConfiguration apiConfiguration;
private final Osi2PostfachProperties.ProxyConfiguration proxyConfiguration;
private final Validator validator;
@PostConstruct
public void validateConfiguration() {
validateConfiguration(authConfiguration);
validateConfiguration(apiConfiguration);
if (proxyConfiguration.isEnabled()) {
validateConfiguration(proxyConfiguration);
}
}
private <T> void validateConfiguration(T configuration) {
var violations = validator.validate(configuration);
if (!violations.isEmpty()) {
throw new TechnicalException(
"%s is invalid: %s".formatted(configuration.getClass().getSimpleName(), formatConstraintValidation(violations)));
}
}
private <T> String formatConstraintValidation(Set<ConstraintViolation<T>> constraints) {
return constraints.stream()
.map(violation -> String.format("%s: %s", violation.getPropertyPath(), violation.getMessage()))
.reduce((a, b) -> a + ", " + b)
.orElse("");
}
}
...@@ -17,7 +17,7 @@ import org.junit.jupiter.params.provider.MethodSource; ...@@ -17,7 +17,7 @@ import org.junit.jupiter.params.provider.MethodSource;
import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.common.errorhandling.TechnicalException;
class Osi2PropertiesValidatorTest { class ApiClientConfigurationTest {
private static final Validator VALIDATOR; private static final Validator VALIDATOR;
...@@ -34,27 +34,27 @@ class Osi2PropertiesValidatorTest { ...@@ -34,27 +34,27 @@ class Osi2PropertiesValidatorTest {
@DisplayName("should return if is valid") @DisplayName("should return if is valid")
@Test @Test
void shouldReturnIfIsValid() { void shouldReturnIfIsValid() {
var validator = new Osi2PropertiesValidator( var validator = new ApiClientConfiguration(
createAuthConfiguration(), createAuthConfiguration(),
createApiConfiguration(), createApiConfiguration(),
createProxyConfiguration(), createProxyConfiguration(),
VALIDATOR VALIDATOR
); );
assertThatCode(validator::validateConfiguration).doesNotThrowAnyException(); assertThatCode(validator::getErrorMessageOfViolations).doesNotThrowAnyException();
} }
@DisplayName("should return if is valid with disabled proxy") @DisplayName("should return if is valid with disabled proxy")
@Test @Test
void shouldReturnIfIsValidWithDisabledProxy() { void shouldReturnIfIsValidWithDisabledProxy() {
var validator = new Osi2PropertiesValidator( var validator = new ApiClientConfiguration(
createAuthConfiguration(), createAuthConfiguration(),
createApiConfiguration(), createApiConfiguration(),
createDisabledProxyConfiguration(), createDisabledProxyConfiguration(),
VALIDATOR VALIDATOR
); );
assertThatCode(validator::validateConfiguration).doesNotThrowAnyException(); assertThatCode(validator::getErrorMessageOfViolations).doesNotThrowAnyException();
} }
static Stream<Arguments> invalidValidatorConfigurations() { static Stream<Arguments> invalidValidatorConfigurations() {
...@@ -85,14 +85,14 @@ class Osi2PropertiesValidatorTest { ...@@ -85,14 +85,14 @@ class Osi2PropertiesValidatorTest {
Osi2PostfachProperties.ApiConfiguration apiConfiguration, Osi2PostfachProperties.ApiConfiguration apiConfiguration,
Osi2PostfachProperties.ProxyConfiguration proxyConfiguration Osi2PostfachProperties.ProxyConfiguration proxyConfiguration
) { ) {
var validator = new Osi2PropertiesValidator( var validator = new ApiClientConfiguration(
authConfiguration, authConfiguration,
apiConfiguration, apiConfiguration,
proxyConfiguration, proxyConfiguration,
VALIDATOR VALIDATOR
); );
assertThatThrownBy(validator::validateConfiguration) assertThatThrownBy(validator::getErrorMessageOfViolations)
.isInstanceOf(TechnicalException.class) .isInstanceOf(TechnicalException.class)
.hasMessageContaining("is invalid"); .hasMessageContaining("is invalid");
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment