diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/IdpCertProvider.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/IdpCertProvider.java index 7aaed7a2..f2969e74 100644 --- a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/IdpCertProvider.java +++ b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/IdpCertProvider.java @@ -1,7 +1,13 @@ /* (C)2023 */ package it.pagopa.tech.lollipop.consumer.idp; +import it.pagopa.tech.lollipop.consumer.exception.CertDataNotFoundException; +import it.pagopa.tech.lollipop.consumer.exception.CertDataTagListNotFoundException; +import it.pagopa.tech.lollipop.consumer.model.IdpCertData; +import java.util.List; + public interface IdpCertProvider { - boolean getIdpCertData(String assertionInstant, String entityId); + List getIdpCertData(String assertionInstant, String entityId) + throws CertDataTagListNotFoundException, CertDataNotFoundException; } diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderFactoryImpl.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderFactoryImpl.java new file mode 100644 index 00000000..121da224 --- /dev/null +++ b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderFactoryImpl.java @@ -0,0 +1,31 @@ +/* (C)2023 */ +package it.pagopa.tech.lollipop.consumer.idp.impl; + +import it.pagopa.tech.lollipop.consumer.idp.IdpCertProvider; +import it.pagopa.tech.lollipop.consumer.idp.IdpCertProviderFactory; +import it.pagopa.tech.lollipop.consumer.idp.client.IdpCertClientProvider; +import javax.inject.Inject; + +/** + * Implementation of {@link IdpCertProviderFactory}, used to create instances of {@link + * IdpCertProviderImpl} + */ +public class IdpCertProviderFactoryImpl implements IdpCertProviderFactory { + + private final IdpCertClientProvider idpCertClientProvider; + + @Inject + public IdpCertProviderFactoryImpl(IdpCertClientProvider idpCertClientProvider) { + this.idpCertClientProvider = idpCertClientProvider; + } + + /** + * Factory for creating an instance of {@link IdpCertProvider} + * + * @return an instance of {@link IdpCertProviderImpl} + */ + @Override + public IdpCertProvider create() { + return new IdpCertProviderImpl(idpCertClientProvider.provideClient()); + } +} diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderFactoryImplStub.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderFactoryImplStub.java deleted file mode 100644 index c72c7da6..00000000 --- a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderFactoryImplStub.java +++ /dev/null @@ -1,18 +0,0 @@ -/* (C)2023 */ -package it.pagopa.tech.lollipop.consumer.idp.impl; - -import it.pagopa.tech.lollipop.consumer.idp.IdpCertProvider; -import it.pagopa.tech.lollipop.consumer.idp.IdpCertProviderFactory; -import it.pagopa.tech.lollipop.consumer.idp.client.IdpCertClientProvider; -import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageProvider; - -public class IdpCertProviderFactoryImplStub implements IdpCertProviderFactory { - - private IdpCertClientProvider idpCertClientProvider; - private IdpCertStorageProvider idpCertStorageProvider; - - @Override - public IdpCertProvider create() { - return null; - } -} diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderImpl.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderImpl.java new file mode 100644 index 00000000..8e269ca3 --- /dev/null +++ b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderImpl.java @@ -0,0 +1,56 @@ +/* (C)2023 */ +package it.pagopa.tech.lollipop.consumer.idp.impl; + +import it.pagopa.tech.lollipop.consumer.exception.CertDataNotFoundException; +import it.pagopa.tech.lollipop.consumer.exception.CertDataTagListNotFoundException; +import it.pagopa.tech.lollipop.consumer.idp.IdpCertProvider; +import it.pagopa.tech.lollipop.consumer.idp.client.IdpCertClient; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageConfig; +import it.pagopa.tech.lollipop.consumer.model.IdpCertData; +import java.util.List; +import javax.inject.Inject; + +public class IdpCertProviderImpl implements IdpCertProvider { + + private IdpCertClient idpCertClient; + + @Inject + public IdpCertProviderImpl(IdpCertClient idpCertClient) { + this.idpCertClient = idpCertClient; + } + + /** + * {@inheritDoc} + * + *

Retrieve the certification data of the given entityId issued in the same timeframe as the + * issue instant of the SAML assertion, first looking in the storage if enabled ({@link + * IdpCertStorageConfig}) and then, if not found, through the client {@link IdpCertClient}. If + * the storage is enabled ({@link IdpCertStorageConfig}) the IdpCertData will be stored, after + * being retrieved by the client. + * + * @param entityId Identity Provider ID + * @param assertionInstant Assertion Issue Instant + * @return the certifications issued before and after the timestamp instant + * @throws CertDataTagListNotFoundException if an error occurred retrieving the list of tags or + * filtering the tags with the instant + * @throws CertDataNotFoundException if an error occurred retrieving the certification XML or if + * data for the given entityId were not found + */ + @Override + public List getIdpCertData(String assertionInstant, String entityId) + throws CertDataTagListNotFoundException, CertDataNotFoundException { + if (assertionInstant == null + || assertionInstant.isBlank() + || entityId == null + || entityId.isBlank()) { + String errMsg = + String.format( + "Cannot retrieve the identity provider cert data, assertion instant" + + " [%s] or entity id [%s] missing", + assertionInstant, entityId); + throw new IllegalArgumentException(errMsg); + } + + return idpCertClient.getCertData(assertionInstant, entityId); + } +} diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderImplStub.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderImplStub.java deleted file mode 100644 index b6df7d6f..00000000 --- a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/impl/IdpCertProviderImplStub.java +++ /dev/null @@ -1,17 +0,0 @@ -/* (C)2023 */ -package it.pagopa.tech.lollipop.consumer.idp.impl; - -import it.pagopa.tech.lollipop.consumer.idp.IdpCertProvider; -import it.pagopa.tech.lollipop.consumer.idp.client.IdpCertClient; -import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorage; - -public class IdpCertProviderImplStub implements IdpCertProvider { - - private IdpCertClient idpCertClient; - private IdpCertStorage idpCertStorage; - - @Override - public boolean getIdpCertData(String assertionInstant, String entityId) { - return false; - } -} diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorage.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorage.java index 6226daf2..ef2f827a 100644 --- a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorage.java +++ b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorage.java @@ -2,15 +2,26 @@ package it.pagopa.tech.lollipop.consumer.idp.storage; import it.pagopa.tech.lollipop.consumer.model.IdpCertData; -import java.util.List; +/** + * Interface of the storage used for storing the identity provider certification data retrieved for + * validation + */ public interface IdpCertStorage { - List getTagList(); - - void saveTagList(List tagList); - + /** + * Retrieve the idp cert data associated with the provided tag + * + * @param tag + * @return the list of idpCertData found + */ IdpCertData getIdpCertData(String tag); - void saveIdpCertData(String tag); + /** + * Store the provided idpCertData + * + * @param tag the idpCertData issue instance + * @param idpCertData + */ + void saveIdpCertData(String tag, IdpCertData idpCertData); } diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorageConfig.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorageConfig.java new file mode 100644 index 00000000..f6fe13a4 --- /dev/null +++ b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorageConfig.java @@ -0,0 +1,14 @@ +/* (C)2023 */ +package it.pagopa.tech.lollipop.consumer.idp.storage; + +import java.util.concurrent.TimeUnit; +import lombok.Data; + +/** Configuration class for the idpCertData storage */ +@Data +public class IdpCertStorageConfig { + + private boolean idpCertDataStorageEnabled = true; + private long storageEvictionDelay = 1L; + private TimeUnit storageEvictionDelayTimeUnit = TimeUnit.MINUTES; +} diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorageProvider.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorageProvider.java index 3f045332..c73d835a 100644 --- a/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorageProvider.java +++ b/core/src/main/java/it/pagopa/tech/lollipop/consumer/idp/storage/IdpCertStorageProvider.java @@ -1,7 +1,11 @@ /* (C)2023 */ package it.pagopa.tech.lollipop.consumer.idp.storage; +/** Interface for the provider used to create instances of {@link IdpCertStorage} */ public interface IdpCertStorageProvider { - IdpCertStorage provideStorage(); + /** + * @return instance of {@link IdpCertStorage} + */ + IdpCertStorage provideStorage(IdpCertStorageConfig storageConfig); } diff --git a/core/src/main/java/it/pagopa/tech/lollipop/consumer/model/IdpCertData.java b/core/src/main/java/it/pagopa/tech/lollipop/consumer/model/IdpCertData.java index 6c0aaa2e..12d0c7ab 100644 --- a/core/src/main/java/it/pagopa/tech/lollipop/consumer/model/IdpCertData.java +++ b/core/src/main/java/it/pagopa/tech/lollipop/consumer/model/IdpCertData.java @@ -1,6 +1,7 @@ /* (C)2023 */ package it.pagopa.tech.lollipop.consumer.model; +import java.util.List; import lombok.Getter; import lombok.Setter; @@ -10,5 +11,5 @@ public class IdpCertData { private String entityId; private String tag; - private String certData; + private List certData; } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 5337fc06..2ce666da 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -245,14 +245,6 @@ - - - - - - - - @@ -345,14 +337,6 @@ - - - - - - - - @@ -377,9 +361,9 @@ + - diff --git a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClient.java b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClient.java index 1b62a68c..e30a4a4a 100644 --- a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClient.java +++ b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClient.java @@ -9,6 +9,8 @@ import it.pagopa.tech.lollipop.consumer.idp.client.simple.internal.model.CertData; import it.pagopa.tech.lollipop.consumer.idp.client.simple.internal.model.EntitiesDescriptor; import it.pagopa.tech.lollipop.consumer.idp.client.simple.internal.model.EntityDescriptor; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorage; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageConfig; import it.pagopa.tech.lollipop.consumer.model.IdpCertData; import java.util.ArrayList; import java.util.Collections; @@ -20,16 +22,22 @@ public class IdpCertSimpleClient implements IdpCertClient { private final DefaultApi defaultApi; private final IdpCertSimpleClientConfig entityConfig; + private IdpCertStorage storage; @Inject - public IdpCertSimpleClient(ApiClient client, IdpCertSimpleClientConfig entityConfig) { + public IdpCertSimpleClient( + ApiClient client, IdpCertSimpleClientConfig entityConfig, IdpCertStorage storage) { this.defaultApi = new DefaultApi(client); this.entityConfig = entityConfig; + this.storage = storage; } /** * Retrieve the certification data of the given entityId issued in the same timeframe as the - * issue instant of the SAML assertion + * issue instant of the SAML assertion, first looking in the storage if enabled ({@link + * IdpCertStorageConfig}) and then, if not found, through the client {@link IdpCertClient}. If + * the storage is enabled ({@link IdpCertStorageConfig}) the IdpCertData will be stored, after + * being retrieved by the client. * * @param entityId Identity Provider ID * @param instant Assertion Issue Instant @@ -61,7 +69,14 @@ public List getCertData(String entityId, String instant) for (String tag : tagList) { try { - IdpCertData certData = getCIECertData(tag, entityId); + String storageTag = codifyStorageTag(tag, entityId); + IdpCertData certData = storage.getIdpCertData(storageTag); + + if (certData == null) { + certData = getCIECertData(tag, entityId); + } else { + storage.saveIdpCertData(storageTag, certData); + } listCertData.add(certData); } catch (ApiException | EntityIdNotFoundException e) { @@ -85,8 +100,14 @@ public List getCertData(String entityId, String instant) for (String tag : tagList) { try { - IdpCertData certData = getSPIDCertData(tag, entityId); + String storageTag = codifyStorageTag(tag, entityId); + IdpCertData certData = storage.getIdpCertData(codifyStorageTag(tag, entityId)); + if (certData == null) { + certData = getSPIDCertData(tag, entityId); + } else { + storage.saveIdpCertData(storageTag, certData); + } listCertData.add(certData); } catch (ApiException | EntityIdNotFoundException e) { throw new CertDataNotFoundException( @@ -147,7 +168,7 @@ private IdpCertData getEntityData(EntitiesDescriptor data, String tag, String en if (entity.getEntityID().equals(entityId)) { newData.setEntityId(entityId); newData.setTag(tag); - newData.setCertData(entity.getSignature()); + newData.setCertData(entity.getSignatureList()); return newData; } @@ -211,4 +232,8 @@ private List getTagsFromInstant(List tagList, String instant) return newTagList; } + + private String codifyStorageTag(String tag, String entityId) { + return tag + entityId; + } } diff --git a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClientProvider.java b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClientProvider.java index 6a292a71..6a9f5ba1 100644 --- a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClientProvider.java +++ b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClientProvider.java @@ -4,6 +4,8 @@ import it.pagopa.tech.lollipop.consumer.idp.client.IdpCertClient; import it.pagopa.tech.lollipop.consumer.idp.client.IdpCertClientProvider; import it.pagopa.tech.lollipop.consumer.idp.client.simple.internal.ApiClient; +import it.pagopa.tech.lollipop.consumer.idp.client.simple.storage.SimpleIdpCertStorageProvider; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageConfig; import javax.inject.Inject; /** Provider class for retrieving an instance of {@link IdpCertSimpleClient} */ @@ -22,6 +24,10 @@ public IdpCertSimpleClientProvider(IdpCertSimpleClientConfig config) { */ @Override public IdpCertClient provideClient() { - return new IdpCertSimpleClient(new ApiClient(this.idpClientConfig), this.idpClientConfig); + SimpleIdpCertStorageProvider storageProvider = new SimpleIdpCertStorageProvider(); + return new IdpCertSimpleClient( + new ApiClient(this.idpClientConfig), + this.idpClientConfig, + storageProvider.provideStorage(new IdpCertStorageConfig())); } } diff --git a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/internal/model/EntityDescriptor.java b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/internal/model/EntityDescriptor.java index f54361ce..8e8b7d1d 100644 --- a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/internal/model/EntityDescriptor.java +++ b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/internal/model/EntityDescriptor.java @@ -3,10 +3,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; import lombok.Getter; /** EntityDescriptor */ @@ -21,7 +19,7 @@ public class EntityDescriptor { @JsonProperty("entityID") private String entityID; - private String signature; + private List signatureList; /** * Methods that obtains the signature used for verify the user's assertion from a series of @@ -32,24 +30,52 @@ public class EntityDescriptor { @SuppressWarnings("unchecked") @JsonProperty("IDPSSODescriptor") private void unpackNestedSignature(Map signature) { - Map keyDescriptor = new HashMap<>(); + List> keyDescriptorsList = new ArrayList<>(); if (signature.get(KEY_DESCRIPTOR) instanceof List) { - List> listDescriptors = - (List>) signature.get(KEY_DESCRIPTOR); - Optional> optionalFirst = - listDescriptors.stream() - .filter(el -> el.get("use").equals("signing")) - .findFirst(); - - if (optionalFirst.isPresent()) { - keyDescriptor = optionalFirst.get(); - } + + keyDescriptorsList = + ((List>) signature.get(KEY_DESCRIPTOR)) + .stream() + .filter(el -> el.get("use").equals("signing")) + .collect(Collectors.toList()); } else { - keyDescriptor = (Map) signature.get(KEY_DESCRIPTOR); + Map keyDescriptorFound = + (Map) signature.get(KEY_DESCRIPTOR); + + if (keyDescriptorFound.get("use").equals("signing")) { + keyDescriptorsList.add(keyDescriptorFound); + } + } + + List> keyInfosList = new ArrayList<>(); + for (Map keyDescriptor : keyDescriptorsList) { + if (keyDescriptor.get(KEY_INFO) instanceof List) { + keyInfosList = (List>) keyDescriptor.get(KEY_INFO); + } else { + Map keyInfo = (Map) keyDescriptor.get(KEY_INFO); + + keyInfosList.add(keyInfo); + } + } + + List> listX509Data = new ArrayList<>(); + for (Map keyInfo : keyInfosList) { + if (keyInfo.get(X_509_DATA) instanceof List) { + listX509Data = (List>) keyInfo.get(X_509_DATA); + } else { + listX509Data.add((Map) keyInfo.get(X_509_DATA)); + } + } + + List signatureList = new ArrayList<>(); + for (Map x509Data : listX509Data) { + if (x509Data.get(X_509_CERTIFICATE) instanceof List) { + signatureList = (List) x509Data.get(X_509_CERTIFICATE); + } else { + signatureList.add((String) x509Data.get(X_509_CERTIFICATE)); + } } - Map keyInfo = (Map) keyDescriptor.get(KEY_INFO); - Map x509Data = (Map) keyInfo.get(X_509_DATA); - this.signature = (String) x509Data.get(X_509_CERTIFICATE); + this.signatureList = signatureList; } } diff --git a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorage.java b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorage.java new file mode 100644 index 00000000..69cc2826 --- /dev/null +++ b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorage.java @@ -0,0 +1,105 @@ +/* (C)2023 */ +package it.pagopa.tech.lollipop.consumer.idp.client.simple.storage; + +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorage; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageConfig; +import it.pagopa.tech.lollipop.consumer.model.IdpCertData; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import javax.inject.Inject; + +/** + * Implementation of the {@link IdpCertStorage} interface as a simple in memory storage. + * + *

The storage can be configured via the {@link IdpCertStorageConfig} configuration class. + * + *

It store in a in memory {@link java.util.HashMap} the assertions and the associated scheduled + * eviction operations, every time an assertion is accessed the associated eviction operation is + * rescheduled. + */ +public class SimpleIdpCertStorage implements IdpCertStorage { + + private final Map idpCertDataMap; + private final Map> scheduledEvictionsMap; + private final IdpCertStorageConfig storageConfig; + + @Inject + public SimpleIdpCertStorage( + Map idpCertDataMap, + Map> scheduledEvictionsMap, + IdpCertStorageConfig storageConfig) { + this.idpCertDataMap = idpCertDataMap; + this.scheduledEvictionsMap = scheduledEvictionsMap; + this.storageConfig = storageConfig; + } + + /** + * Retrieve the idpCertData associated with the provided tag if the storage is enabled {@link + * IdpCertStorageConfig}, otherwise no operation is performed. + * + *

Before the list of idpCertData is returned the associated eviction operation is + * rescheduled with the delay configured via {@link IdpCertStorageConfig} + * + * @param tag the idpCertData issue instant + * @return the list of cert data if found, null if no cert data are present in the storage or + * the storage is disabled + */ + @Override + public IdpCertData getIdpCertData(String tag) { + if (!storageConfig.isIdpCertDataStorageEnabled()) { + return null; + } + + IdpCertData idpCertData = idpCertDataMap.get(tag); + if (idpCertData != null) { + delayEviction(tag); + } + return idpCertData; + } + + /** + * Store the idpCertData if the storage is enabled {@link IdpCertStorageConfig}, otherwise no + * operation is performed. + * + *

Once the idpCertData is stored an eviction operation is scheduled with a delay configured + * via {@link IdpCertStorageConfig} + * + * @param tag the idpCertData issue instant + * @param idpCertData + */ + @Override + public void saveIdpCertData(String tag, IdpCertData idpCertData) { + if (!storageConfig.isIdpCertDataStorageEnabled()) { + return; + } + + idpCertDataMap.put(tag, idpCertData); + scheduleEviction(tag); + } + + private void scheduleEviction(String assertionRef) { + ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + ScheduledFuture schedule = + executorService.schedule( + getEvictionTask(assertionRef), + storageConfig.getStorageEvictionDelay(), + storageConfig.getStorageEvictionDelayTimeUnit()); + scheduledEvictionsMap.put(assertionRef, schedule); + } + + private void delayEviction(String tag) { + ScheduledFuture schedule = scheduledEvictionsMap.get(tag); + schedule.cancel(false); + scheduledEvictionsMap.remove(tag); + scheduleEviction(tag); + } + + private Runnable getEvictionTask(String tag) { + return () -> { + idpCertDataMap.remove(tag); + scheduledEvictionsMap.remove(tag); + }; + } +} diff --git a/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorageProvider.java b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorageProvider.java new file mode 100644 index 00000000..710f5787 --- /dev/null +++ b/identity-service-rest-client-native/src/main/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorageProvider.java @@ -0,0 +1,22 @@ +/* (C)2023 */ +package it.pagopa.tech.lollipop.consumer.idp.client.simple.storage; + +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorage; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageConfig; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageProvider; +import java.util.HashMap; + +/** Implementation of {@link IdpCertStorageProvider} interface. It provides an instance of the */ +public class SimpleIdpCertStorageProvider implements IdpCertStorageProvider { + + /** + * {@inheritDoc} + * + * @param storageConfig the storage configuration + * @return an instance of {@link SimpleIdpCertStorage} + */ + @Override + public IdpCertStorage provideStorage(IdpCertStorageConfig storageConfig) { + return new SimpleIdpCertStorage(new HashMap<>(), new HashMap<>(), storageConfig); + } +} diff --git a/identity-service-rest-client-native/src/test/java/simple/IdpCertSimpleClientTest.java b/identity-service-rest-client-native/src/test/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClientTest.java similarity index 63% rename from identity-service-rest-client-native/src/test/java/simple/IdpCertSimpleClientTest.java rename to identity-service-rest-client-native/src/test/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClientTest.java index 20a12462..e4617299 100644 --- a/identity-service-rest-client-native/src/test/java/simple/IdpCertSimpleClientTest.java +++ b/identity-service-rest-client-native/src/test/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/IdpCertSimpleClientTest.java @@ -1,11 +1,11 @@ /* (C)2023 */ -package simple; +package it.pagopa.tech.lollipop.consumer.idp.client.simple; import it.pagopa.tech.lollipop.consumer.exception.CertDataNotFoundException; import it.pagopa.tech.lollipop.consumer.exception.CertDataTagListNotFoundException; -import it.pagopa.tech.lollipop.consumer.idp.client.simple.IdpCertSimpleClient; -import it.pagopa.tech.lollipop.consumer.idp.client.simple.IdpCertSimpleClientConfig; import it.pagopa.tech.lollipop.consumer.idp.client.simple.internal.ApiClient; +import it.pagopa.tech.lollipop.consumer.idp.client.simple.storage.SimpleIdpCertStorageProvider; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageConfig; import it.pagopa.tech.lollipop.consumer.model.IdpCertData; import java.time.Instant; import java.util.List; @@ -21,6 +21,7 @@ class IdpCertSimpleClientTest { private static final String INSTANT = String.valueOf(Instant.now().getEpochSecond()); private static final String SPID_ENTITY_ID = "https://posteid.poste.it"; + private static final String SPID_ENTITY_ID_MULTIPLE_SIGNATURE = "https://loginspid.aruba.it"; private static final String CIE_ENTITY_ID = "https://idserver.servizicie.interno.gov.it/idp/profile/SAML2/POST/SSO"; @@ -31,7 +32,12 @@ class IdpCertSimpleClientTest { public static void startServer() { entityConfig = Mockito.spy(IdpCertSimpleClientConfig.builder().build()); ApiClient client = new ApiClient(entityConfig); - idpCertSimpleClient = new IdpCertSimpleClient(client, entityConfig); + SimpleIdpCertStorageProvider storageProvider = new SimpleIdpCertStorageProvider(); + idpCertSimpleClient = + new IdpCertSimpleClient( + client, + entityConfig, + storageProvider.provideStorage(new IdpCertStorageConfig())); } @Test @@ -41,6 +47,16 @@ void certSPIDDataFound() throws CertDataTagListNotFoundException, CertDataNotFou Assertions.assertNotNull(response); } + @Test + void certSPIDDataFoundMultipleSignature() + throws CertDataTagListNotFoundException, CertDataNotFoundException { + List response = + idpCertSimpleClient.getCertData(SPID_ENTITY_ID_MULTIPLE_SIGNATURE, INSTANT); + + Assertions.assertNotNull(response); + Assertions.assertTrue(response.get(0).getCertData().size() > 1); + } + @Test void certCIEDataFound() throws CertDataTagListNotFoundException, CertDataNotFoundException { List response = idpCertSimpleClient.getCertData(CIE_ENTITY_ID, INSTANT); @@ -68,4 +84,18 @@ void getCIECertDataWrongInstant() { CertDataTagListNotFoundException.class, () -> idpCertSimpleClient.getCertData(CIE_ENTITY_ID, WRONG_INSTANT)); } + + @Test + void entityIdNull() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> idpCertSimpleClient.getCertData(null, WRONG_INSTANT)); + } + + @Test + void instantNull() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> idpCertSimpleClient.getCertData(CIE_ENTITY_ID, null)); + } } diff --git a/identity-service-rest-client-native/src/test/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorageTest.java b/identity-service-rest-client-native/src/test/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorageTest.java new file mode 100644 index 00000000..e189d7eb --- /dev/null +++ b/identity-service-rest-client-native/src/test/java/it/pagopa/tech/lollipop/consumer/idp/client/simple/storage/SimpleIdpCertStorageTest.java @@ -0,0 +1,135 @@ +/* (C)2023 */ +package it.pagopa.tech.lollipop.consumer.idp.client.simple.storage; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorage; +import it.pagopa.tech.lollipop.consumer.idp.storage.IdpCertStorageConfig; +import it.pagopa.tech.lollipop.consumer.model.IdpCertData; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.*; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SimpleIdpCertStorageTest { + + private static final long EVICTION_DELAY = 5000L; + private static IdpCertStorageConfig storageConfigMock; + private IdpCertStorage sut; + private static final String IDPCERTDATA_1 = "1680691737https://posteid.poste.it"; + + @BeforeEach + void setUp() { + storageConfigMock = mock(IdpCertStorageConfig.class); + doReturn(EVICTION_DELAY).when(storageConfigMock).getStorageEvictionDelay(); + doReturn(TimeUnit.MILLISECONDS).when(storageConfigMock).getStorageEvictionDelayTimeUnit(); + } + + @Test + void getExistingAssertionAndResetScheduleEvictionWithStorageEnabled() + throws InterruptedException, ExecutionException { + doReturn(true).when(storageConfigMock).isIdpCertDataStorageEnabled(); + + Map idpCertDataMap = new HashMap<>(); + IdpCertData idpCertData = new IdpCertData(); + idpCertDataMap.put(IDPCERTDATA_1, idpCertData); + Map> scheduledEvictionsMap = new HashMap<>(); + ScheduledFuture scheduledFutureMock = mock(ScheduledFuture.class); + scheduledEvictionsMap.put(IDPCERTDATA_1, scheduledFutureMock); + + sut = new SimpleIdpCertStorage(idpCertDataMap, scheduledEvictionsMap, storageConfigMock); + + IdpCertData result = sut.getIdpCertData(IDPCERTDATA_1); + + assertNotNull(result); + assertEquals(idpCertData, result); + assertEquals(1, scheduledEvictionsMap.size()); + + CompletableFuture future = waitEvictionEnd(scheduledEvictionsMap); + assertEquals(true, future.get()); + assertEquals(0, idpCertDataMap.size()); + assertEquals(0, scheduledEvictionsMap.size()); + } + + @Test + void getNotExistingAssertionWithStorageEnabled() { + doReturn(true).when(storageConfigMock).isIdpCertDataStorageEnabled(); + + sut = new SimpleIdpCertStorage(new HashMap<>(), new HashMap<>(), storageConfigMock); + + IdpCertData result = sut.getIdpCertData(IDPCERTDATA_1); + + assertNull(result); + } + + @Test + void saveAssertionAndScheduleEvictionWithStorageEnabled() + throws InterruptedException, ExecutionException { + doReturn(true).when(storageConfigMock).isIdpCertDataStorageEnabled(); + + Map idpCertDataMap = new HashMap<>(); + Map> scheduledEvictionsMap = new HashMap<>(); + + sut = new SimpleIdpCertStorage(idpCertDataMap, scheduledEvictionsMap, storageConfigMock); + IdpCertData idpCertData = new IdpCertData(); + + sut.saveIdpCertData(IDPCERTDATA_1, idpCertData); + + assertEquals(1, idpCertDataMap.size()); + assertEquals(1, scheduledEvictionsMap.size()); + assertEquals(idpCertData, idpCertDataMap.get(IDPCERTDATA_1)); + + CompletableFuture future = waitEvictionEnd(scheduledEvictionsMap); + assertEquals(true, future.get()); + assertEquals(0, idpCertDataMap.size()); + assertEquals(0, scheduledEvictionsMap.size()); + } + + @Test + void getAssertionWithStorageDisabled() { + doReturn(false).when(storageConfigMock).isIdpCertDataStorageEnabled(); + + sut = new SimpleIdpCertStorage(new HashMap<>(), new HashMap<>(), storageConfigMock); + + IdpCertData result = sut.getIdpCertData(IDPCERTDATA_1); + + assertNull(result); + } + + @Test + void savaAssertionWithStorageDisabled() { + doReturn(false).when(storageConfigMock).isIdpCertDataStorageEnabled(); + + Map idpCertDataMap = new HashMap<>(); + Map> scheduledEvictionsMap = new HashMap<>(); + + sut = new SimpleIdpCertStorage(idpCertDataMap, scheduledEvictionsMap, storageConfigMock); + + sut.saveIdpCertData(IDPCERTDATA_1, new IdpCertData()); + + assertEquals(0, idpCertDataMap.size()); + assertEquals(0, scheduledEvictionsMap.size()); + } + + private CompletableFuture waitEvictionEnd( + Map> scheduledEvictionsMap) { + CompletableFuture future = new CompletableFuture<>(); + ExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + executorService.submit( + new Runnable() { + @SneakyThrows + @Override + public void run() { + ScheduledFuture scheduledFuture = + scheduledEvictionsMap.get(IDPCERTDATA_1); + scheduledFuture.get(); + future.complete(true); + } + }); + return future; + } +}