Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IS-271 Now using PkiCredentialFactory bean #272

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@
import se.swedenconnect.bankid.rpapi.service.impl.BankIDClientImpl;
import se.swedenconnect.bankid.rpapi.service.impl.ZxingQRGenerator;
import se.swedenconnect.bankid.rpapi.support.WebClientFactoryBean;
import se.swedenconnect.security.credential.PkiCredential;
import se.swedenconnect.security.credential.bundle.CredentialBundles;
import se.swedenconnect.security.credential.config.ConfigurationResourceLoader;
import se.swedenconnect.security.credential.factory.PkiCredentialFactory;
import se.swedenconnect.spring.saml.idp.config.configurers.Saml2IdpConfigurerAdapter;
import se.swedenconnect.spring.saml.idp.extensions.SignatureMessagePreprocessor;
Expand All @@ -71,24 +68,19 @@ public class BankIdConfiguration {
/** BankID configuration properties. */
private final BankIdConfigurationProperties properties;

/** Credential bundles. */
private final CredentialBundles credentialBundles;

/** Resource loader. */
private final ConfigurationResourceLoader resourceLoader;
/** For loading credentials. */
private final PkiCredentialFactory pkiCredentialFactory;

/**
* Constructor.
*
* @param properties the BankID configuration properties
* @param credentialBundles the credential bundles
* @param resourceLoader for loading resources
* @param pkiCredentialFactory for loading credentials
*/
public BankIdConfiguration(final BankIdConfigurationProperties properties, final CredentialBundles credentialBundles,
final ConfigurationResourceLoader resourceLoader) {
public BankIdConfiguration(final BankIdConfigurationProperties properties,
final PkiCredentialFactory pkiCredentialFactory) {
this.properties = Objects.requireNonNull(properties, "properties must not be null");
this.credentialBundles = credentialBundles;
this.resourceLoader = resourceLoader;
this.pkiCredentialFactory = Objects.requireNonNull(pkiCredentialFactory, "pkiCredentialFactory must not be null");
}

/**
Expand Down Expand Up @@ -160,24 +152,6 @@ QRGenerator qrGenerator() {
return generator;
}

/**
* Gets a lambda function that returns the {@link PkiCredential} to use for a specific relying party.
*
* @return a lambda function for creating the credential for a relying party
*/
@Bean
Function<RelyingPartyConfiguration, PkiCredential> relyingPartyCredentialProvider() {
return rp -> {
try {
return PkiCredentialFactory.createCredential(rp.getCredential(), this.resourceLoader,
this.credentialBundles.getCredentialProvider(), this.credentialBundles.getKeyStoreProvider(), null);
}
catch (final Exception e) {
throw new SecurityException("Failed to get credential for relying party", e);
}
};
}

/**
* Gets the bankIdWebClientFactory bean
*
Expand All @@ -190,7 +164,7 @@ Function<RelyingPartyConfiguration, WebClient> bankIdWebClientFactory() {
try {
final WebClientFactoryBean webClientFactory =
new WebClientFactoryBean(this.properties.getServiceUrl(), this.properties.getServerRootCertificate(),
this.relyingPartyCredentialProvider().apply(rp));
this.pkiCredentialFactory.createCredential(rp.getCredential()));
webClientFactory.afterPropertiesSet();
return webClientFactory.createInstance();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import se.swedenconnect.bankid.idp.config.BankIdConfigurationProperties;
import se.swedenconnect.bankid.idp.config.BankIdConfigurationProperties.HealthConfiguration;
import se.swedenconnect.security.credential.PkiCredential;
import se.swedenconnect.security.credential.factory.PkiCredentialFactory;

import java.time.Duration;
import java.time.Instant;
Expand All @@ -30,7 +31,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

/**
* Health check indicator for the installed BankID Relying Party certificates.
Expand All @@ -52,18 +52,18 @@ public class RpCertificateHealthIndicator implements HealthIndicator {
* Constructor.
*
* @param properties the BankID IdP configuration properties
* @param relyingPartyCredentialProvider for getting the RP credential
* @param pkiCredentialFactory for loading credentials
*/
public RpCertificateHealthIndicator(final BankIdConfigurationProperties properties,
final Function<BankIdConfigurationProperties.RelyingPartyConfiguration, PkiCredential> relyingPartyCredentialProvider) {
final PkiCredentialFactory pkiCredentialFactory) {
this.warnThreshold = Optional.ofNullable(properties.getHealth())
.map(HealthConfiguration::getRpCertificateWarnThreshold)
.orElseGet(() -> HealthConfiguration.RP_CERTIFICATE_WARN_THRESHOLD_DEFAULT);
.orElse(HealthConfiguration.RP_CERTIFICATE_WARN_THRESHOLD_DEFAULT);

this.certificateInformation = properties.getRelyingParties().stream()
.map(rp -> {
try {
final PkiCredential credential = relyingPartyCredentialProvider.apply(rp);
final PkiCredential credential = pkiCredentialFactory.createCredential(rp.getCredential());
Objects.requireNonNull(credential);
return new CertificateInformation(rp.getId(), credential.getCertificate().getNotAfter());
}
Expand Down Expand Up @@ -95,10 +95,10 @@ else if (ch.expiresSoon()) {
})
.toList();

if (list.stream().anyMatch(ch -> ch.expired())) {
if (list.stream().anyMatch(CertificateHealth::expired)) {
return builder.down().build();
}
else if (list.stream().anyMatch(ch -> ch.expiresSoon())) {
else if (list.stream().anyMatch(CertificateHealth::expiresSoon)) {
return builder.status(CustomStatus.WARNING).build();
}
else {
Expand All @@ -111,9 +111,7 @@ private static CertificateHealth of(final CertificateInformation certificateInfo
final Duration warnThreshold) {
final Date notAfter = certificateInformation.notAfter();
final boolean expired = notAfter.before(new Date());
final boolean expiresSoon = !expired
? notAfter.before(Date.from(Instant.now().plus(warnThreshold)))
: false;
final boolean expiresSoon = !expired && notAfter.before(Date.from(Instant.now().plus(warnThreshold)));
return new CertificateHealth(certificateInformation.id(), expired, expiresSoon, notAfter);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import se.swedenconnect.bankid.idp.config.BankIdConfigurationProperties;
import se.swedenconnect.bankid.idp.config.BankIdConfigurationProperties.RelyingPartyConfiguration;
import se.swedenconnect.security.credential.PkiCredential;
import se.swedenconnect.security.credential.factory.PkiCredentialConfigurationProperties;
import se.swedenconnect.security.credential.factory.PkiCredentialFactory;

import java.security.cert.X509Certificate;
import java.time.Duration;
Expand All @@ -34,7 +36,6 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
* Test cases for RpCertificateHealthIndicator.
Expand All @@ -54,9 +55,9 @@ public void testUp() throws Exception {
final Rp rp2 = new Rp("ID2", NOW.plus(15, ChronoUnit.DAYS));

final BankIdConfigurationProperties props = buildProps(rp1, rp2);
final Function<RelyingPartyConfiguration, PkiCredential> credProvider = buildCredentialFunction(rp1, rp2);
final PkiCredentialFactory factory = buildPkiCredentialFactory(rp1, rp2);

final RpCertificateHealthIndicator indicator = new RpCertificateHealthIndicator(props, credProvider);
final RpCertificateHealthIndicator indicator = new RpCertificateHealthIndicator(props, factory);

final Health health = indicator.getHealth(true);
Assertions.assertEquals(Status.UP, health.getStatus());
Expand All @@ -80,9 +81,9 @@ public void testExpired() throws Exception {
final Rp rp2 = new Rp("ID2", NOW.minus(2, ChronoUnit.DAYS));

final BankIdConfigurationProperties props = buildProps(rp1, rp2);
final Function<RelyingPartyConfiguration, PkiCredential> credProvider = buildCredentialFunction(rp1, rp2);
final PkiCredentialFactory factory = buildPkiCredentialFactory(rp1, rp2);

final RpCertificateHealthIndicator indicator = new RpCertificateHealthIndicator(props, credProvider);
final RpCertificateHealthIndicator indicator = new RpCertificateHealthIndicator(props, factory);

final Health health = indicator.getHealth(true);
Assertions.assertEquals(Status.DOWN, health.getStatus());
Expand All @@ -107,9 +108,9 @@ public void testWarning() throws Exception {

final BankIdConfigurationProperties props = buildProps(rp1, rp2);
props.getHealth().setRpCertificateWarnThreshold(Duration.ofDays(7));
final Function<RelyingPartyConfiguration, PkiCredential> credProvider = buildCredentialFunction(rp1, rp2);
final PkiCredentialFactory factory = buildPkiCredentialFactory(rp1, rp2);

final RpCertificateHealthIndicator indicator = new RpCertificateHealthIndicator(props, credProvider);
final RpCertificateHealthIndicator indicator = new RpCertificateHealthIndicator(props, factory);

final Health health = indicator.getHealth(true);
Assertions.assertEquals(CustomStatus.WARNING, health.getStatus());
Expand All @@ -125,24 +126,24 @@ public void testWarning() throws Exception {
JSONAssert.assertEquals(expected, json, false);
}

private static BankIdConfigurationProperties buildProps(final Rp... rps) throws Exception {
private static BankIdConfigurationProperties buildProps(final Rp... rps) {
final BankIdConfigurationProperties props = new BankIdConfigurationProperties();
for (final Rp rp : rps) {
final RelyingPartyConfiguration relyingParty = Mockito.mock(RelyingPartyConfiguration.class);
Mockito.when(relyingParty.getId()).thenReturn(rp.id());
final RelyingPartyConfiguration relyingParty = new RelyingPartyConfiguration();
relyingParty.setId(rp.id());
final PkiCredentialConfigurationProperties cp = new PkiCredentialConfigurationProperties();
cp.setBundle(rp.id());
relyingParty.setCredential(cp);
props.getRelyingParties().add(relyingParty);
}

return props;
}

private static Function<RelyingPartyConfiguration, PkiCredential> buildCredentialFunction(final Rp... rps) {
final Map<String, PkiCredential> credentials = new HashMap<>();
private static PkiCredentialFactory buildPkiCredentialFactory(final Rp... rps) {

final Map<String, PkiCredential> credentials = new HashMap<>();
for (final Rp rp : rps) {
final RelyingPartyConfiguration relyingParty = Mockito.mock(RelyingPartyConfiguration.class);
Mockito.when(relyingParty.getId()).thenReturn(rp.id());

final X509Certificate cert = Mockito.mock(X509Certificate.class);
Mockito.when(cert.getNotAfter()).thenReturn(Date.from(rp.expires()));

Expand All @@ -152,7 +153,7 @@ private static Function<RelyingPartyConfiguration, PkiCredential> buildCredentia
credentials.put(rp.id(), cred);
}

return conf -> credentials.get(conf.getId());
return new PkiCredentialFactory(credentials::get, null, null, false);
}

private record Rp(String id, Instant expires) {
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<bc.version>1.80</bc.version>
<zxing.version>3.5.3</zxing.version>
<slf4j.version>2.0.16</slf4j.version>
<credentials-support.version>2.0.3</credentials-support.version>
<credentials-support.version>2.0.4</credentials-support.version>
</properties>

<repositories>
Expand Down
Loading