Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS has been phased out. To see alternatives please check here

Skip to content
Snippets Groups Projects
Commit 284c70b3 authored by Marco Amoia's avatar Marco Amoia
Browse files

Merge branch 'release' into 'main'

Release 1.1.0

See merge request !46
parents 35b4d1bd 5d046a1b
Branches
Tags v1.1.0
2 merge requests!47Update version to 1.2.0,!46Release 1.1.0
Pipeline #270552 failed
Showing
with 1645 additions and 546 deletions
...@@ -7,10 +7,23 @@ data: ...@@ -7,10 +7,23 @@ data:
SPRING_DATASOURCE_USERNAME: "{{ .Values.db.username }}" SPRING_DATASOURCE_USERNAME: "{{ .Values.db.username }}"
SPRING_DATASOURCE_PASSWORD: "{{ .Values.db.password }}" SPRING_DATASOURCE_PASSWORD: "{{ .Values.db.password }}"
SPRING_DATA_REDIS_HOST: "{{ .Values.redis.host }}"
SPRING_DATA_REDIS_PORT: "{{ .Values.redis.port }}"
SPRING_DATA_REDIS_USERNAME: "{{ .Values.redis.username }}"
SPRING_DATA_REDIS_PASSWORD: "{{ .Values.redis.password }}"
KEYPAIR_ALGORITHM: "{{ .Values.keypair.algorithm }}" KEYPAIR_ALGORITHM: "{{ .Values.keypair.algorithm }}"
KEYPAIR_SIGNATURE_ALGORITHM: "{{ .Values.keypair.signatureAlgorithm }}" KEYPAIR_SIGNATURE_ALGORITHM: "{{ .Values.keypair.signatureAlgorithm }}"
KEYPAIR_KEY_LENGTH: "{{ .Values.keypair.keyLength }}" KEYPAIR_KEY_LENGTH: "{{ .Values.keypair.keyLength }}"
MICROSERVICE_USERSROLES_URL: "{{ .Values.microservices.usersRolesUrl }}" MICROSERVICE_USERSROLES_URL: {{ tpl .Values.microservices.usersRolesUrl . | quote }}
{{- if eq .Values.global.profile "authority" }}
MICROSERVICE_IDENTITY_PROVIDER_URL: {{ tpl .Values.microservices.identityProviderUrl . | quote }}
{{- end }}
SIMPL_CERTIFICATE_SAN: "{{ .Values.simpl.certificate.san }}" SIMPL_CERTIFICATE_SAN: "{{ .Values.simpl.certificate.san }}"
CLIENT_AUTHORITY_URL: "{{- include "tls.gateway.url" . }}"
SPRING_PROFILES_ACTIVE: {{ .Values.profile | quote }}
...@@ -114,6 +114,12 @@ db: ...@@ -114,6 +114,12 @@ db:
password: "authenticationprovider" password: "authenticationprovider"
cipherSecret: "authenticationprovider-db-cipher-secret" cipherSecret: "authenticationprovider-db-cipher-secret"
redis:
host: "redis-master.{{ .Release.Namespace }}.svc.cluster.local"
port: "6379"
username: "default"
password: "admin"
keypair: keypair:
signatureAlgorithm: "SHA256withECDSA" signatureAlgorithm: "SHA256withECDSA"
algorithm: "ECDSA" algorithm: "ECDSA"
...@@ -121,7 +127,10 @@ keypair: ...@@ -121,7 +127,10 @@ keypair:
microservices: microservices:
usersRolesUrl: http://users-roles.{{ .Release.Namespace }}.svc.cluster.local:8080 usersRolesUrl: http://users-roles.{{ .Release.Namespace }}.svc.cluster.local:8080
identityProviderUrl: http://identity-provider.{{ .Release.Namespace }}.svc.cluster.local:8080
simpl: simpl:
certificate: certificate:
san: <your-t2-fqdn> san: <your-t2-fqdn>
profile: # authority / participant
\ No newline at end of file
This diff is collapsed.
{
"openapi": "3.0.1",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "https://authentication-provider.authority.svc.cluster.local",
"description": "Generated server url"
}
],
"paths": {
"/keypair": {
"get": {
"tags": [
"keypair-controller"
],
"summary": "Get installed Key Pair",
"description": "ONBOARDER_M user get the public/private Key Pair",
"operationId": "getInstalledKeyPair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "Key Pair successfully retrieved. The response body contains the details of the requested KeyPair.",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
},
"404": {
"description": "Key Pair not found. The requested KeyPair does not exist or is not accessible.",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
}
}
},
"post": {
"tags": [
"keypair-controller"
],
"summary": "Import Key Pair",
"description": "ONBOARDER_M user import a public/private Key Pair and store it to database",
"operationId": "importKeyPair",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ImportKeyPairDTO"
}
}
},
"required": true
},
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"204": {
"description": "Key Pair generate and stored successfully"
},
"401": {
"description": "Unauthorized"
}
}
}
},
"/keypair/generate": {
"post": {
"tags": [
"keypair-controller"
],
"summary": "Generate Key Pair",
"description": "ONBOARDER_M user generate a public/private Key Pair and store it to database",
"operationId": "generateKeyPair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"204": {
"description": "Key Pair generate and stored successfully"
},
"401": {
"description": "Unauthorized"
}
}
}
},
"/csr/generate": {
"post": {
"tags": [
"csr-controller"
],
"operationId": "generateCSR",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DistinguishedNameDTO"
}
}
},
"required": true
},
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/StreamingResponseBody"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"ErrorDTO": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"elementName": {
"type": "string"
}
}
},
"ImportKeyPairDTO": {
"required": [
"privateKey",
"publicKey"
],
"type": "object",
"properties": {
"publicKey": {
"type": "string"
},
"privateKey": {
"type": "string"
}
}
},
"DistinguishedNameDTO": {
"required": [
"commonName",
"country",
"organization",
"organizationalUnit"
],
"type": "object",
"properties": {
"commonName": {
"type": "string"
},
"organization": {
"type": "string"
},
"organizationalUnit": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"StreamingResponseBody": {
"type": "object"
},
"KeyPairDTO": {
"type": "object",
"properties": {
"publicKey": {
"type": "string",
"format": "byte"
},
"privateKey": {
"type": "string",
"format": "byte"
}
}
}
}
}
}
{
"openapi": "3.0.1",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "https://authentication-provider.authority.svc.cluster.local",
"description": "Generated server url"
}
],
"paths": {
"/keypair": {
"get": {
"tags": [
"keypair-controller"
],
"summary": "Get installed Key Pair",
"description": "ONBOARDER_M user get the public/private Key Pair",
"operationId": "getInstalledKeyPair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "Key Pair successfully retrieved. The response body contains the details of the requested KeyPair.",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
},
"404": {
"description": "Key Pair not found. The requested KeyPair does not exist or is not accessible.",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/KeyPairDTO"
}
}
}
}
}
},
"post": {
"tags": [
"keypair-controller"
],
"summary": "Import Key Pair",
"description": "ONBOARDER_M user import a public/private Key Pair and store it to database",
"operationId": "importKeyPair",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ImportKeyPairDTO"
}
}
},
"required": true
},
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"204": {
"description": "Key Pair generate and stored successfully"
},
"401": {
"description": "Unauthorized"
}
}
},
"head": {
"tags": [
"keypair-controller"
],
"summary": "Keypair Exists",
"description": "ONBOARDER_M checks whether a public/private Key Pair is stored in the database",
"operationId": "existsKeypair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "KeyPair is present",
"content": {
"*/*": {
"schema": {
"type": "boolean"
}
}
}
},
"204": {
"description": "KeyPair is not present",
"content": {
"*/*": {
"schema": {
"type": "boolean"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"*/*": {
"schema": {
"type": "boolean"
}
}
}
}
}
}
},
"/keypair/generate": {
"post": {
"tags": [
"keypair-controller"
],
"summary": "Generate Key Pair",
"description": "ONBOARDER_M user generate a public/private Key Pair and store it to database",
"operationId": "generateKeyPair",
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"204": {
"description": "Key Pair generate and stored successfully"
},
"401": {
"description": "Unauthorized"
}
}
}
},
"/csr/generate": {
"post": {
"tags": [
"csr-controller"
],
"operationId": "generateCSR",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DistinguishedNameDTO"
}
}
},
"required": true
},
"responses": {
"409": {
"description": "Conflict",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/ErrorDTO"
}
}
}
},
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/StreamingResponseBody"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"ErrorDTO": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"elementName": {
"type": "string"
}
}
},
"ImportKeyPairDTO": {
"required": [
"privateKey",
"publicKey"
],
"type": "object",
"properties": {
"publicKey": {
"type": "string"
},
"privateKey": {
"type": "string"
}
}
},
"DistinguishedNameDTO": {
"required": [
"commonName",
"country",
"organization",
"organizationalUnit"
],
"type": "object",
"properties": {
"commonName": {
"type": "string"
},
"organization": {
"type": "string"
},
"organizationalUnit": {
"type": "string"
},
"country": {
"type": "string"
}
}
},
"StreamingResponseBody": {
"type": "object"
},
"KeyPairDTO": {
"type": "object",
"properties": {
"publicKey": {
"type": "string",
"format": "byte"
},
"privateKey": {
"type": "string",
"format": "byte"
}
}
}
}
}
}
PROJECT_VERSION_NUMBER="1.0.0" PROJECT_VERSION_NUMBER="1.1.0"
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>eu.europa.ec.simpl</groupId> <groupId>eu.europa.ec.simpl</groupId>
<artifactId>simpl-parent</artifactId> <artifactId>simpl-parent</artifactId>
<version>1.0.0</version> <version>1.1.0</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
...@@ -19,10 +19,26 @@ ...@@ -19,10 +19,26 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency> <dependency>
<groupId>eu.europa.ec.simpl</groupId> <groupId>eu.europa.ec.simpl</groupId>
<artifactId>simpl-spring-boot-starter</artifactId> <artifactId>simpl-spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency>
<groupId>eu.europa.ec.simpl</groupId>
<artifactId>simpl-api-iaa</artifactId>
</dependency>
<dependency>
<groupId>eu.europa.ec.simpl</groupId>
<artifactId>simpl-http-client-feign</artifactId>
</dependency>
<dependency>
<groupId>eu.europa.ec.simpl</groupId>
<artifactId>simpl-http-client-okhttp</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.uuid</groupId> <groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId> <artifactId>java-uuid-generator</artifactId>
...@@ -67,10 +83,6 @@ ...@@ -67,10 +83,6 @@
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <artifactId>mapstruct</artifactId>
......
package eu.europa.ec.simpl.authenticationprovider.configurations; package eu.europa.ec.simpl.authenticationprovider.configurations;
import eu.europa.ec.simpl.api.identityprovider.v1.exchanges.MtlsApi;
import eu.europa.ec.simpl.api.usersroles.v1.exchanges.IdentityAttributesApi;
import eu.europa.ec.simpl.api.usersroles.v1.exchanges.PublicKeysApi;
import eu.europa.ec.simpl.common.annotations.Authority;
import eu.europa.ec.simpl.common.exchanges.usersroles.CredentialExchange; import eu.europa.ec.simpl.common.exchanges.usersroles.CredentialExchange;
import eu.europa.ec.simpl.common.messageconverters.StreamingResponseBodyMessageConverter;
import java.net.URI;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter; import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory; import org.springframework.web.service.invoker.HttpServiceProxyFactory;
@Log4j2
@Configuration @Configuration
public class ClientConfig { public class ClientConfig {
private static final String V1_PREFIX = "v1";
private final MicroserviceProperties properties;
public ClientConfig(MicroserviceProperties properties) {
this.properties = properties;
}
@Bean
@Authority
public MtlsApi tierOnePublicKeyApi(RestClient.Builder restClientBuilder) {
return buildExchange(properties.identityProvider().url().resolve(V1_PREFIX), restClientBuilder, MtlsApi.class);
}
@Bean
public PublicKeysApi publicKeysApi(RestClient.Builder restClientBuilder) {
return buildExchange(properties.usersRoles().url().resolve(V1_PREFIX), restClientBuilder, PublicKeysApi.class);
}
@Bean
public CredentialExchange credentialExchange(RestClient.Builder restClientBuilder) {
return buildExchange(
properties.usersRoles().url(),
restClientBuilder.messageConverters(l -> l.add(new StreamingResponseBodyMessageConverter())),
CredentialExchange.class);
}
@Bean @Bean
public CredentialExchange credentialExchange( public IdentityAttributesApi usersRolesIdentityAttributesApi(RestClient.Builder restClientBuilder) {
MicroserviceProperties properties, RestClient.Builder restClientBuilder) { return buildExchange(
return buildExchange(properties.usersRoles().url(), restClientBuilder, CredentialExchange.class); properties.usersRoles().url().resolve(V1_PREFIX), restClientBuilder, IdentityAttributesApi.class);
} }
private <E> E buildExchange(String baseurl, RestClient.Builder restClientBuilder, Class<E> clazz) { private static <E> E buildExchange(URI baseurl, RestClient.Builder restClientBuilder, Class<E> clazz) {
var restClient = restClientBuilder.baseUrl(baseurl).build(); var restClient = restClientBuilder.baseUrl(baseurl).build();
var adapter = RestClientAdapter.create(restClient); var adapter = RestClientAdapter.create(restClient);
var factory = HttpServiceProxyFactory.builderFor(adapter).build(); var factory = HttpServiceProxyFactory.builderFor(adapter).build();
......
package eu.europa.ec.simpl.authenticationprovider.configurations; package eu.europa.ec.simpl.authenticationprovider.configurations;
import eu.europa.ec.simpl.common.model.csr.AlgorithmConfig; import eu.europa.ec.simpl.common.csr.AlgorithmConfig;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "keypair") @ConfigurationProperties(prefix = "keypair")
......
package eu.europa.ec.simpl.authenticationprovider.configurations; package eu.europa.ec.simpl.authenticationprovider.configurations;
import java.net.URI;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "microservice") @ConfigurationProperties(prefix = "microservice")
public record MicroserviceProperties(UsersRoles usersRoles) { public record MicroserviceProperties(UsersRoles usersRoles, IdentityProvider identityProvider) {
public record UsersRoles(String url) {} public record UsersRoles(URI url) {}
public record IdentityProvider(URI url) {}
} }
package eu.europa.ec.simpl.authenticationprovider.configurations.mtls;
import eu.europa.ec.simpl.authenticationprovider.services.CredentialService;
import eu.europa.ec.simpl.authenticationprovider.services.KeyPairService;
import eu.europa.ec.simpl.common.exchanges.mtls.AuthorityExchange;
import eu.europa.ec.simpl.common.exchanges.mtls.ParticipantExchange;
import eu.europa.ec.simpl.common.utils.CredentialUtil;
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
@Log4j2
@Component
public class MtlsClientBuilder {
private final MtlsClientFactory mtlsClientFactory;
private final CredentialService credentialService;
private final MtlsClientProperties mtlsClientProperties;
private final KeyPairService keyPairService;
public MtlsClientBuilder(
MtlsClientFactory mtlsClientFactory,
CredentialService credentialService,
MtlsClientProperties mtlsClientProperties,
KeyPairService keyPairService) {
this.mtlsClientFactory = mtlsClientFactory;
this.credentialService = credentialService;
this.mtlsClientProperties = mtlsClientProperties;
this.keyPairService = keyPairService;
}
public AuthorityExchange buildAuthorityClient() {
return mtlsClientFactory.buildAuthorityClient(
mtlsClientProperties.authority().url(), readKeyStore());
}
public ParticipantExchange buildParticipantClient(String url) {
return mtlsClientFactory.buildParticipantClient(url, readKeyStore());
}
private KeyStore readKeyStore() {
if (!credentialService.hasCredential()) {
log.error("No credential found");
throw new IllegalStateException("No credential found");
}
return CredentialUtil.loadCredential(
new ByteArrayInputStream(credentialService.getCredential()), getPrivateKey());
}
private PrivateKey getPrivateKey() {
var privateKey = keyPairService.getInstalledKeyPair().getPrivateKey();
return CredentialUtil.loadPrivateKey(privateKey, "EC");
}
}
package eu.europa.ec.simpl.authenticationprovider.configurations.mtls;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.europa.ec.simpl.client.core.SimplClient;
import eu.europa.ec.simpl.client.core.adapters.EphemeralProofAdapter;
import eu.europa.ec.simpl.client.core.ssl.SslInfo;
import eu.europa.ec.simpl.client.feign.FeignSimplClient;
import eu.europa.ec.simpl.client.okhttp.OkHttpSimplClient;
import eu.europa.ec.simpl.common.exchanges.mtls.AuthorityExchange;
import eu.europa.ec.simpl.common.exchanges.mtls.ParticipantExchange;
import eu.europa.ec.simpl.common.interceptors.TierOneTokenPropagatorInterceptor;
import java.security.KeyStore;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
@Log4j2
@Component
public class MtlsClientFactory {
private final OkHttpSimplClient okHttpSimplClient;
private final FeignSimplClient feignSimplClient;
private final TierOneTokenPropagatorInterceptor tokenPropagator;
private final MtlsClientProperties clientProperties;
private final EphemeralProofAdapter ephemeralProofAdapter;
private final ObjectMapper objectMapper;
public MtlsClientFactory(
OkHttpSimplClient okHttpSimplClient,
FeignSimplClient feignSimplClient,
TierOneTokenPropagatorInterceptor tokenPropagator,
MtlsClientProperties clientProperties,
EphemeralProofAdapter ephemeralProofAdapter,
ObjectMapper objectMapper) {
this.okHttpSimplClient = okHttpSimplClient;
this.feignSimplClient = feignSimplClient;
this.tokenPropagator = tokenPropagator;
this.clientProperties = clientProperties;
this.ephemeralProofAdapter = ephemeralProofAdapter;
this.objectMapper = objectMapper;
}
public AuthorityExchange buildAuthorityClient(String url, KeyStore keyStore) {
log.info("Creating MTLS Client of type {}", clientProperties.type());
return switch (clientProperties.type()) {
case FEIGN -> buildClient(feignSimplClient.builder(), keyStore).target(AuthorityExchange.class, url);
case OKHTTP -> {
var client = buildClient(okHttpSimplClient.builder(), keyStore);
yield new OkHttpAuthorityExchange(
client.build(), clientProperties.authority().url(), objectMapper);
}
};
}
public ParticipantExchange buildParticipantClient(String url, KeyStore keyStore) {
return buildAuthorityClient(url, keyStore);
}
private <T> T buildClient(SimplClient.Builder<T> clientBuilder, KeyStore keyStore) {
return clientBuilder
.setSslInfoSupplier(() -> new SslInfo(keyStore))
.setAuthorizationHeaderSupplier(tokenPropagator)
.setEphemeralProofAdapter(ephemeralProofAdapter)
.setAuthorityUrlSupplier(() -> clientProperties.authority().url())
.build();
}
}
package eu.europa.ec.simpl.authenticationprovider.configurations.mtls;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "client")
public record MtlsClientProperties(AuthorityProperties authority, Type type) {
public record AuthorityProperties(String url) {}
public enum Type {
FEIGN,
OKHTTP
}
}
package eu.europa.ec.simpl.authenticationprovider.configurations.mtls;
import eu.europa.ec.simpl.client.feign.DaggerFeignSimplClientFactory;
import eu.europa.ec.simpl.client.feign.FeignSimplClient;
import eu.europa.ec.simpl.client.okhttp.DaggerOkHttpSimplClientFactory;
import eu.europa.ec.simpl.client.okhttp.OkHttpSimplClient;
import eu.europa.ec.simpl.common.exchanges.mtls.AuthorityExchange;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
@Configuration
public class MtlsConfig implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope("mtls", mtlsScope());
}
@Bean
public MtlsScope mtlsScope() {
return new MtlsScope();
}
@Bean
@Scope(value = "mtls", proxyMode = ScopedProxyMode.TARGET_CLASS)
public AuthorityExchange authorityClient(MtlsClientBuilder mtlsClientBuilder) {
return mtlsClientBuilder.buildAuthorityClient();
}
@Bean
public FeignSimplClient feignSimplClient() {
return DaggerFeignSimplClientFactory.create().get();
}
@Bean
public OkHttpSimplClient okHttpSimplClient() {
return DaggerOkHttpSimplClientFactory.create().get();
}
}
package eu.europa.ec.simpl.authenticationprovider.configurations.mtls;
import eu.europa.ec.simpl.authenticationprovider.event.InvalidateMtls;
import eu.europa.ec.simpl.common.exchanges.mtls.AuthorityExchange;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.context.event.EventListener;
@Log4j2
public class MtlsScope implements Scope {
private AuthorityExchange authorityExchange;
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (authorityExchange == null) {
log.info("Building new mtls client");
authorityExchange = (AuthorityExchange) objectFactory.getObject();
}
return authorityExchange;
}
@Override
public Object remove(String name) {
return null;
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
// Optional operation
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
@EventListener(InvalidateMtls.class)
public void invalidEvent() {
log.info("Invalidate authority mtls client");
authorityExchange = null;
}
}
package eu.europa.ec.simpl.authenticationprovider.configurations.mtls;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.europa.ec.simpl.common.exceptions.RuntimeWrapperException;
import eu.europa.ec.simpl.common.exchanges.mtls.AuthorityExchange;
import eu.europa.ec.simpl.common.model.dto.identityprovider.ParticipantDTO;
import eu.europa.ec.simpl.common.model.dto.identityprovider.ParticipantWithIdentityAttributesDTO;
import eu.europa.ec.simpl.common.model.dto.securityattributesprovider.IdentityAttributeDTO;
import eu.europa.ec.simpl.common.model.dto.securityattributesprovider.IdentityAttributeWithOwnershipDTO;
import java.io.IOException;
import java.util.List;
import okhttp3.*;
public class OkHttpAuthorityExchange implements AuthorityExchange {
private final OkHttpClient client;
private final String authorityUrl;
private final ObjectMapper objectMapper;
public OkHttpAuthorityExchange(OkHttpClient client, String authorityUrl, ObjectMapper objectMapper) {
this.client = client;
this.authorityUrl = authorityUrl;
this.objectMapper = objectMapper;
}
@Override
public String token() {
var body = RequestBody.create(new byte[0], null);
var request = new Request.Builder()
.post(body)
.url(authorityUrl + "/identity-api/mtls/token")
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
} catch (IOException e) {
throw new RuntimeWrapperException(e);
}
}
@Override
public ParticipantDTO sendTierOnePublicKey(String tierOnePublicKey) {
var body = RequestBody.create(tierOnePublicKey.getBytes(), MediaType.parse("text/plain"));
var request = new Request.Builder()
.patch(body)
.url(authorityUrl + "/identity-api/mtls/public-key")
.build();
try (Response response = client.newCall(request).execute()) {
return objectMapper.readValue(response.body().byteStream(), ParticipantDTO.class);
} catch (IOException e) {
throw new RuntimeWrapperException(e);
}
}
@Override
public ParticipantWithIdentityAttributesDTO echo() {
var json = doGet("/identity-api/mtls/echo");
return convert(json, ParticipantWithIdentityAttributesDTO.class);
}
@Override
public List<IdentityAttributeWithOwnershipDTO> getIdentityAttributesWithOwnership() {
var json = doGet("/sap-api/mtls/identity-attribute");
return convert(json, new TypeReference<List<IdentityAttributeWithOwnershipDTO>>() {});
}
@Override
public List<IdentityAttributeDTO> getIdentityAttributesByCredentialIdInUri(String certificateId) {
var json = doGet("/sap-api/mtls/identity-attribute/" + certificateId);
return convert(json, new TypeReference<List<IdentityAttributeDTO>>() {});
}
@Override
public ParticipantWithIdentityAttributesDTO ping() {
var json = doGet("/user-api/mtls/ping");
return convert(json, ParticipantWithIdentityAttributesDTO.class);
}
private String doGet(String path) {
var request = new Request.Builder().get().url(authorityUrl + path).build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
} catch (IOException e) {
throw new RuntimeWrapperException(e);
}
}
private <T> T convert(String json, Class<T> clazz) {
try {
return objectMapper.readValue(json, clazz);
} catch (JsonProcessingException e) {
throw new RuntimeWrapperException(e);
}
}
private <T> T convert(String json, TypeReference<T> typeReference) {
try {
return objectMapper.readValue(json, typeReference);
} catch (JsonProcessingException e) {
throw new RuntimeWrapperException(e);
}
}
}
package eu.europa.ec.simpl.authenticationprovider.controllers;
import eu.europa.ec.simpl.authenticationprovider.services.AgentService;
import eu.europa.ec.simpl.common.exchanges.authenticationprovider.AgentControllerExchange;
import eu.europa.ec.simpl.common.model.dto.authenticationprovider.EchoDTO;
import eu.europa.ec.simpl.common.model.dto.identityprovider.ParticipantWithIdentityAttributesDTO;
import eu.europa.ec.simpl.common.model.dto.securityattributesprovider.IdentityAttributeDTO;
import eu.europa.ec.simpl.common.model.dto.securityattributesprovider.IdentityAttributeWithOwnershipDTO;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AgentController implements AgentControllerExchange {
private final AgentService agentService;
public AgentController(AgentService agentService) {
this.agentService = agentService;
}
@Override
public EchoDTO echo() {
return agentService.echo();
}
@Override
public List<IdentityAttributeWithOwnershipDTO> getIdentityAttributesWithOwnership() {
return agentService.getAndSyncIdentityAttributes();
}
@Override
public List<IdentityAttributeDTO> getParticipantIdentityAttributes(@PathVariable String credentialId) {
return agentService.getParticipantIdentityAttributes(credentialId);
}
@Override
public ParticipantWithIdentityAttributesDTO ping(@RequestParam String fqdn) {
return agentService.ping(fqdn);
}
}
package eu.europa.ec.simpl.authenticationprovider.controllers;
import eu.europa.ec.simpl.api.authenticationprovider.v1.exchanges.AgentsApi;
import eu.europa.ec.simpl.api.authenticationprovider.v1.model.EchoDTO;
import eu.europa.ec.simpl.api.authenticationprovider.v1.model.IdentityAttributeDTO;
import eu.europa.ec.simpl.api.authenticationprovider.v1.model.IdentityAttributeWithOwnershipDTO;
import eu.europa.ec.simpl.api.authenticationprovider.v1.model.ParticipantWithIdentityAttributesDTO;
import eu.europa.ec.simpl.authenticationprovider.mappers.AgentMapperV1;
import eu.europa.ec.simpl.authenticationprovider.services.AgentService;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("v1")
public class AgentControllerV1 implements AgentsApi {
private final AgentController controller;
private final AgentService agentService;
private final AgentMapperV1 mapper;
public AgentControllerV1(AgentController controller, AgentMapperV1 mapper, AgentService agentService) {
this.controller = controller;
this.mapper = mapper;
this.agentService = agentService;
}
@Override
public EchoDTO echo() {
return mapper.toV1(controller.echo());
}
@Override
public List<IdentityAttributeWithOwnershipDTO> getIdentityAttributesWithOwnership() {
return mapper.toListIdentityAttributeWithOwnershipDTOV1(controller.getIdentityAttributesWithOwnership());
}
@Override
public List<IdentityAttributeDTO> getParticipantIdentityAttributes(String credentialId) {
return mapper.toListIdentityAttributeDTOV1(controller.getParticipantIdentityAttributes(credentialId));
}
@Override
public ParticipantWithIdentityAttributesDTO pingAgent(String fqdn) {
return mapper.toV1(controller.ping(fqdn));
}
@Override
public String requestEphemeralProofFromAuthority() {
return agentService.requestEphemeralProofFromAuthority();
}
@Override
public void validateCredential(String body) {
agentService.validateCredential(body);
}
}
...@@ -2,7 +2,7 @@ package eu.europa.ec.simpl.authenticationprovider.controllers; ...@@ -2,7 +2,7 @@ package eu.europa.ec.simpl.authenticationprovider.controllers;
import eu.europa.ec.simpl.authenticationprovider.services.CSRService; import eu.europa.ec.simpl.authenticationprovider.services.CSRService;
import eu.europa.ec.simpl.common.exchanges.authenticationprovider.CSRExchange; import eu.europa.ec.simpl.common.exchanges.authenticationprovider.CSRExchange;
import eu.europa.ec.simpl.common.model.dto.DistinguishedNameDTO; import eu.europa.ec.simpl.common.model.dto.authenticationprovider.DistinguishedNameDTO;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.util.Optional; import java.util.Optional;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
......
package eu.europa.ec.simpl.authenticationprovider.controllers;
import eu.europa.ec.simpl.api.authenticationprovider.v1.exchanges.CertificateSignRequestsApi;
import eu.europa.ec.simpl.api.authenticationprovider.v1.model.DistinguishedNameDTO;
import eu.europa.ec.simpl.authenticationprovider.mappers.CSRMapperV1;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@RestController
@RequestMapping("v1")
public class CSRControllerV1 implements CertificateSignRequestsApi {
private final CSRController controller;
private final CSRMapperV1 mapper;
public CSRControllerV1(CSRController controller, CSRMapperV1 mapper) {
this.controller = controller;
this.mapper = mapper;
}
@Override
public StreamingResponseBody generateCSR(DistinguishedNameDTO distinguishedNameDTO) {
return controller.generateCSR(mapper.toV0(distinguishedNameDTO));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment