dev.fitko.fitconnect.client.bootstrap.ClientFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of client Show documentation
Show all versions of client Show documentation
Library that provides client access to the FIT-Connect api-endpoints for sending, subscribing and
routing
package dev.fitko.fitconnect.client.bootstrap;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.RSAKey;
import dev.fitko.fitconnect.api.FitConnectService;
import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.config.SenderConfig;
import dev.fitko.fitconnect.api.config.SubscriberConfig;
import dev.fitko.fitconnect.api.config.ZBPCertConfig;
import dev.fitko.fitconnect.api.config.chunking.AttachmentChunkingConfig;
import dev.fitko.fitconnect.api.config.defaults.ZBPEnvironment;
import dev.fitko.fitconnect.api.config.http.HttpConfig;
import dev.fitko.fitconnect.api.config.http.ProxyConfig;
import dev.fitko.fitconnect.api.config.http.RetryConfig;
import dev.fitko.fitconnect.api.config.http.Timeouts;
import dev.fitko.fitconnect.api.config.resources.BuildInfo;
import dev.fitko.fitconnect.api.config.resources.CertificateConfig;
import dev.fitko.fitconnect.api.domain.model.reply.Reply;
import dev.fitko.fitconnect.api.domain.model.submission.Submission;
import dev.fitko.fitconnect.api.exceptions.client.FitConnectInitialisationException;
import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.api.services.crypto.CryptoService;
import dev.fitko.fitconnect.api.services.crypto.MessageDigestService;
import dev.fitko.fitconnect.api.services.events.CaseService;
import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
import dev.fitko.fitconnect.api.services.events.SecurityEventService;
import dev.fitko.fitconnect.api.services.http.HttpClient;
import dev.fitko.fitconnect.api.services.keys.KeyService;
import dev.fitko.fitconnect.api.services.reply.ReplyService;
import dev.fitko.fitconnect.api.services.routing.RoutingService;
import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
import dev.fitko.fitconnect.api.services.submission.SubmissionService;
import dev.fitko.fitconnect.api.services.validation.ValidationService;
import dev.fitko.fitconnect.client.RouterClient;
import dev.fitko.fitconnect.client.SenderClient;
import dev.fitko.fitconnect.client.SubscriberClient;
import dev.fitko.fitconnect.client.ZBPClient;
import dev.fitko.fitconnect.client.zbp.ZBPServiceAdapter;
import dev.fitko.fitconnect.client.attachments.AttachmentPayloadHandler;
import dev.fitko.fitconnect.client.attachments.AttachmentStorageResolver;
import dev.fitko.fitconnect.client.attachments.download.AttachmentDownloader;
import dev.fitko.fitconnect.client.attachments.download.ReplyAttachmentDownloader;
import dev.fitko.fitconnect.client.attachments.download.SubmissionAttachmentDownloader;
import dev.fitko.fitconnect.client.attachments.upload.AttachmentUploader;
import dev.fitko.fitconnect.client.attachments.upload.ReplyAttachmentUploader;
import dev.fitko.fitconnect.client.attachments.upload.SubmissionAttachmentUploader;
import dev.fitko.fitconnect.client.sender.ReplyReceiver;
import dev.fitko.fitconnect.client.sender.SubmissionSender;
import dev.fitko.fitconnect.client.subscriber.ReplySender;
import dev.fitko.fitconnect.client.subscriber.SubmissionReceiver;
import dev.fitko.fitconnect.client.util.DestinationValidator;
import dev.fitko.fitconnect.client.util.ResourceLoadingUtils;
import dev.fitko.fitconnect.core.FitConnectDefaultService;
import dev.fitko.fitconnect.core.auth.DefaultOAuthApiService;
import dev.fitko.fitconnect.core.cases.CaseApiService;
import dev.fitko.fitconnect.core.cases.EventLogVerifier;
import dev.fitko.fitconnect.core.cases.SecurityEventTokenService;
import dev.fitko.fitconnect.core.crypto.HashService;
import dev.fitko.fitconnect.core.crypto.JWECryptoService;
import dev.fitko.fitconnect.core.http.DefaultHttpClient;
import dev.fitko.fitconnect.core.http.interceptors.ApiRequestInterceptor;
import dev.fitko.fitconnect.core.http.interceptors.RetryInterceptor;
import dev.fitko.fitconnect.core.http.interceptors.UserAgentInterceptor;
import dev.fitko.fitconnect.core.io.FileChunker;
import dev.fitko.fitconnect.core.keys.PublicKeyApiService;
import dev.fitko.fitconnect.core.reply.ReplyApiService;
import dev.fitko.fitconnect.core.routing.RouteVerifier;
import dev.fitko.fitconnect.core.routing.RoutingApiService;
import dev.fitko.fitconnect.core.schema.SchemaResourceProvider;
import dev.fitko.fitconnect.core.submission.SubmissionApiService;
import dev.fitko.fitconnect.core.validation.DefaultValidationService;
import dev.fitko.fitconnect.core.zbp.ZBPApiService;
import okhttp3.Interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static dev.fitko.fitconnect.client.util.ResourceLoadingUtils.loadAllSchemaResources;
import static dev.fitko.fitconnect.client.util.ResourceLoadingUtils.loadYaml;
import static dev.fitko.fitconnect.client.util.ResourceLoadingUtils.readKeyFromPath;
import static java.util.Objects.requireNonNull;
/**
* Factory that constructs {@link SenderClient}, {@link SubscriberClient} and {@link RouterClient}.
*/
public final class ClientFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(ClientFactory.class);
private static final String BUILD_INFO_PATH = "buildinfo.yaml";
private static final String CERTIFICATE_CONFIG_PATH = "certificate-config.yaml";
private static final String SCHEMA_CONFIG_PATH = "schema-config.yaml";
private ClientFactory() {
}
/**
* Create a new {@link SenderClient} to send submissions and receive replies.
* The client is automatically configured via the provided {@link ApplicationConfig}.
*
* @return the sender client
* @throws FitConnectInitialisationException on errors during the sender client construction
*/
public static SenderClient createSenderClient(final ApplicationConfig config) throws FitConnectInitialisationException {
requireNonNull(config, "Application config must not be null");
LOGGER.info("Initializing sender client ...");
final SenderConfig senderConfig = config.getSenderConfig();
final FitConnectService fitConnectService = createFitConnectService(config, senderConfig.getClientId(), senderConfig.getClientSecret());
final DestinationValidator destinationValidator = new DestinationValidator(fitConnectService);
final AttachmentChunkingConfig chunkingConfig = config.getAttachmentChunkingConfig();
final AttachmentStorageResolver attachmentStorageResolver = new AttachmentStorageResolver(chunkingConfig.getAttachmentStoragePath());
final AttachmentPayloadHandler attachmentPayloadHandler = createPayloadHandler(chunkingConfig, attachmentStorageResolver);
final AttachmentUploader attachmentUploader = new SubmissionAttachmentUploader(fitConnectService);
final AttachmentDownloader attachmentDownloader = new ReplyAttachmentDownloader(config, fitConnectService, attachmentPayloadHandler);
final SubmissionSender submissionSender = new SubmissionSender(config, fitConnectService, destinationValidator, attachmentPayloadHandler, attachmentUploader);
final ReplyReceiver replyReceiver = new ReplyReceiver(config, fitConnectService, attachmentDownloader);
return new SenderClient(fitConnectService, submissionSender, replyReceiver, attachmentStorageResolver);
}
/**
* Create a new {@link SubscriberClient} to receive submissions and send replies.
* The client is automatically configured via the provided {@link ApplicationConfig}.
*
* @return the subscriber client
* @throws FitConnectInitialisationException on errors during the subscriber client construction
*/
public static SubscriberClient createSubscriberClient(final ApplicationConfig config) throws FitConnectInitialisationException {
requireNonNull(config, "Application config must not be null");
LOGGER.info("Initializing subscriber client ...");
final SubscriberConfig subscriberConfig = config.getSubscriberConfig();
final RSAKey rsaDecryptionKey = getDecryptionKeyFromConfig(subscriberConfig);
final RSAKey rsaSigningKey = getSignatureKeyFromConfig(subscriberConfig);
final FitConnectService fitConnectService = createFitConnectService(config, subscriberConfig.getClientId(), subscriberConfig.getClientSecret(), rsaSigningKey);
final DestinationValidator destinationValidator = new DestinationValidator(fitConnectService);
final AttachmentChunkingConfig chunkingConfig = config.getAttachmentChunkingConfig();
final AttachmentStorageResolver attachmentStorageResolver = new AttachmentStorageResolver(chunkingConfig.getAttachmentStoragePath());
final AttachmentPayloadHandler attachmentPayloadHandler = createPayloadHandler(chunkingConfig, attachmentStorageResolver);
final AttachmentUploader attachmentUploader = new ReplyAttachmentUploader(fitConnectService);
final AttachmentDownloader attachmentDownloader = new SubmissionAttachmentDownloader(config, fitConnectService, attachmentPayloadHandler);
final SubmissionReceiver submissionReceiver = new SubmissionReceiver(fitConnectService, rsaDecryptionKey, config, attachmentDownloader);
final ReplySender replySender = new ReplySender(config, fitConnectService, destinationValidator, attachmentPayloadHandler, attachmentUploader);
return new SubscriberClient(fitConnectService, submissionReceiver, replySender, attachmentStorageResolver);
}
/**
* Create a new {@link RouterClient} to find destinations and services that is automatically configured via a provided {@link ApplicationConfig}.
*
* @return the routing client
* @throws FitConnectInitialisationException on errors during the routing client construction
*/
public static RouterClient createRouterClient(final ApplicationConfig config) throws FitConnectInitialisationException {
requireNonNull(config, "Application config must not be null");
LOGGER.info("Initializing router client ...");
final HttpClient httpClient = createHttpClient(config);
final SchemaProvider schemaProvider = createSchemaProvider(httpClient, config);
final ValidationService validator = createValidationService(config, schemaProvider, new HashService());
final KeyService keyService = new PublicKeyApiService(config, httpClient, validator);
final RouteVerifier routeVerifier = new RouteVerifier(keyService, validator);
final RoutingService routingService = new RoutingApiService(httpClient, config.getRoutingBaseUrl());
return new RouterClient(routingService, routeVerifier);
}
/**
* Create a new client to send messages and status updates to mailboxes of the ZBP (Zentrales Bürgerpostfach).
*
* @param certConfig the certificate configuration holding the private signing key and the public key
* @param environment the environment requests are sent to
* @return the client
* @throws FitConnectInitialisationException on errors during the zbp client construction
*/
public static ZBPClient createZBPClient(ZBPCertConfig certConfig, ZBPEnvironment environment) {
return createZBPClient(certConfig, environment, HttpConfig.builder().build());
}
/**
* Create a new client to send messages and status updates to mailboxes of the ZBP (Zentrales Bürgerpostfach).
*
* @param certConfig the certificate configuration holding the private signing key and the public key
* @param environment the environment requests are sent to
* @param httpConfig HTTP-client configuration to pass custom {@link RetryConfig}, {@link Timeouts} or {@link ProxyConfig} settings
* @return the client
* @throws FitConnectInitialisationException on errors during the zbp client construction
*/
public static ZBPClient createZBPClient(ZBPCertConfig certConfig, ZBPEnvironment environment, HttpConfig httpConfig) {
LOGGER.info("Initializing ZBP client ...");
final HttpClient httpClient = createHttpClient(ApplicationConfig.builder().httpConfig(httpConfig).build(), certConfig);
final ZBPApiService apiService = new ZBPApiService(httpClient, environment.getBaseUrl());
return new ZBPClient(new ZBPServiceAdapter(apiService, certConfig.getClientPrivateKey()));
}
private static FitConnectService createFitConnectService(final ApplicationConfig config, final String clientId, final String clientSecret) {
return createFitConnectService(config, clientId, clientSecret, null);
}
private static FitConnectService createFitConnectService(final ApplicationConfig config, final String clientId, final String clientSecret, final RSAKey signingKey) {
final HttpClient httpClient = createHttpClient(config);
final SchemaProvider schemaProvider = createSchemaProvider(httpClient, config);
final MessageDigestService messageDigestService = new HashService();
final CryptoService cryptoService = new JWECryptoService(messageDigestService);
final ValidationService validator = createValidationService(config, schemaProvider, messageDigestService);
final OAuthService authService = new DefaultOAuthApiService(httpClient, clientId, clientSecret, config.getAuthBaseUrl());
final SubmissionService submissionService = new SubmissionApiService(authService, httpClient, config.getSubmissionBaseUrl());
final KeyService keyService = new PublicKeyApiService(config, httpClient, authService, validator);
final EventLogVerificationService eventLogVerifier = new EventLogVerifier(keyService, validator);
final CaseService caseService = new CaseApiService(authService, httpClient, eventLogVerifier, config.getSubmissionBaseUrl());
final ReplyService replyService = new ReplyApiService(authService, httpClient, config.getSubmissionBaseUrl());
final SecurityEventService setService = new SecurityEventTokenService(config, validator, signingKey);
return new FitConnectDefaultService(submissionService, caseService, replyService, cryptoService, validator, keyService, setService);
}
private static ValidationService createValidationService(final ApplicationConfig config, final SchemaProvider schemaProvider, final MessageDigestService messageDigestService) {
final List trustedRootCertificates = loadYaml(CERTIFICATE_CONFIG_PATH, CertificateConfig.class)
.getTrustedRootCertificates().stream()
.map(ResourceLoadingUtils::readResourceToString)
.collect(Collectors.toList());
LOGGER.info("Initialised trusted root certificates");
return new DefaultValidationService(config, messageDigestService, schemaProvider, trustedRootCertificates);
}
private static HttpClient createHttpClient(final ApplicationConfig config) {
return createHttpClient(config, null);
}
private static HttpClient createHttpClient(final ApplicationConfig config, ZBPCertConfig zbpCertConfig) {
final BuildInfo buildInfo = loadYaml(BUILD_INFO_PATH, BuildInfo.class);
final HttpConfig httpConfig = config.getHttpConfig();
final List interceptors = getInterceptors(buildInfo, httpConfig);
return new DefaultHttpClient(httpConfig, interceptors, zbpCertConfig);
}
private static List getInterceptors(BuildInfo buildInfo, HttpConfig httpConfig) {
final List interceptors = new ArrayList<>();
interceptors.add(new ApiRequestInterceptor());
interceptors.add(new UserAgentInterceptor(buildInfo));
final RetryConfig retryConfig = httpConfig.getRetryConfig();
if (retryConfig.isAllowRetries()) {
LOGGER.info("Using {} retries for failed HTTP-calls", retryConfig.getMaxRetryCount());
interceptors.add(new RetryInterceptor(retryConfig));
}
return interceptors;
}
private static SchemaProvider createSchemaProvider(final HttpClient httpClient, final ApplicationConfig config) {
final var schemaResources = loadAllSchemaResources(SCHEMA_CONFIG_PATH, config.getSubmissionDataSchemas());
return new SchemaResourceProvider(httpClient, schemaResources);
}
private static AttachmentPayloadHandler createPayloadHandler(final AttachmentChunkingConfig config, AttachmentStorageResolver attachmentStorageResolver) {
final FileChunker fileChunker = new FileChunker();
final MessageDigestService messageDigestService = new HashService();
return new AttachmentPayloadHandler(config, fileChunker, attachmentStorageResolver, messageDigestService);
}
private static RSAKey parseRSAKeyFromString(final String key) {
try {
return RSAKey.parse(JWK.parse(key).toJSONObject());
} catch (final ParseException | NullPointerException e) {
LOGGER.error("Could not parse key, please check environment (config.yml)");
throw new FitConnectInitialisationException("Key could not be parsed", e);
}
}
private static String getPrivateDecryptionKeyPathFromSubscriber(final SubscriberConfig subscriberConfig) {
if (subscriberConfig.getPrivateDecryptionKeyPaths().size() != 1) {
throw new FitConnectInitialisationException("Currently only one configured private key per subscriber is allowed !");
}
return subscriberConfig.getPrivateDecryptionKeyPaths().get(0);
}
private static RSAKey getSignatureKeyFromConfig(SubscriberConfig config) {
LOGGER.info("Initialising private signature key");
if (config.getSubscriberKeys() != null) {
return config.getSubscriberKeys().getPrivateSigningKey().toRSAKey();
}
return parseRSAKeyFromString(readKeyFromPath(config.getPrivateSigningKeyPath()));
}
private static RSAKey getDecryptionKeyFromConfig(SubscriberConfig config) {
LOGGER.info("Initialising private decryption key");
if (config.getSubscriberKeys() != null) {
return config.getSubscriberKeys().getPrivateDecryptionKeys().get(0).toRSAKey();
}
final String decryptionKeyPath = getPrivateDecryptionKeyPathFromSubscriber(config);
final String decryptionKey = readKeyFromPath(decryptionKeyPath);
return parseRSAKeyFromString(decryptionKey);
}
}