diff --git a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java index 44055f539cfd293632e33f3f14a0a3bc22a58641..4cdc1977698bc2936053ecbda04eb7bca4157cbb 100644 --- a/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java +++ b/nachrichten-manager-server/src/main/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfiguration.java @@ -1,8 +1,7 @@ /* - * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * Copyright (c) 2024. Das Land Schleswig-Holstein + * vertreten durch den Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei Abteilung Digitalisierung und zentrales IT-Management der Landesregierung * * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - @@ -36,6 +35,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import jakarta.annotation.PostConstruct; @@ -139,7 +139,7 @@ class BayernIdSamlConfiguration { CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials); return new ExplicitKeySignatureTrustEngine(credentialsResolver, - DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver()); + DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver()); } CriteriaSet getVerificationCriteria() { @@ -180,12 +180,9 @@ class BayernIdSamlConfiguration { List<Saml2X509Credential> getCertificatesFromMetadata() { try (var metadata = antragraumProperties.getMetadataUri().getInputStream()) { var xmlObject = xmlObject(metadata); - if (xmlObject instanceof EntitiesDescriptor descriptors) { - for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) { - if (descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) != null) { - return getVerificationCertificates(descriptor); - } - } + var descriptorOptional = findEntityDescriptor(xmlObject); + if (descriptorOptional.isPresent()) { + return getVerificationCertificates(descriptorOptional.get()); } } catch (IOException e) { throw new Saml2Exception("Error reading idp metadata.", e); @@ -210,6 +207,17 @@ class BayernIdSamlConfiguration { } } + private Optional<EntityDescriptor> findEntityDescriptor(XMLObject metadata) { + Optional<EntityDescriptor> descriptor = Optional.empty(); + if (metadata instanceof EntityDescriptor) { + descriptor = Optional.of((EntityDescriptor) metadata); + } else if (metadata instanceof EntitiesDescriptor) { + descriptor = ((EntitiesDescriptor) metadata).getEntityDescriptors().stream().findFirst(); + } + + return descriptor.filter(entityDescriptor -> entityDescriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) != null).stream().findFirst(); + } + List<Saml2X509Credential> getVerificationCertificates(EntityDescriptor descriptor) { var idpssoDescriptor = descriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS); if (idpssoDescriptor == null) { @@ -226,7 +234,7 @@ class BayernIdSamlConfiguration { } if (verification.isEmpty()) { throw new Saml2Exception( - "Metadata response is missing verification certificates, necessary for verifying SAML assertions"); + "Metadata response is missing verification certificates, necessary for verifying SAML assertions"); } return verification; diff --git a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java index 045f5caf46429366373499cfd279563895d3d9ad..0bac1da357e1ab413b2dbc3d9f019ffc2fc13407 100644 --- a/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java +++ b/nachrichten-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/BayernIdSamlConfigurationTest.java @@ -1,5 +1,8 @@ /* - * Copyright (c) 2024. + * Copyright (c) 2024. Das Land Schleswig-Holstein + * vertreten durch den Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); @@ -41,7 +44,7 @@ class BayernIdSamlConfigurationTest { @Nested class TestCreationOfParserPool { @BeforeEach - void setup() throws Exception { + void setup() { bayernIdSamlConfiguration.initOpenSAML(); } @@ -54,7 +57,7 @@ class BayernIdSamlConfigurationTest { @Nested class TestCreatingTrustEngine { @BeforeEach - void setup() throws Exception { + void setup() { when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml"))); when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de/"); bayernIdSamlConfiguration.initOpenSAML(); @@ -67,7 +70,7 @@ class BayernIdSamlConfigurationTest { } @Nested - class TestLoadingVerficationData { + class TestLoadingVerificationData { @Test void shouldCreateVerificationCriteria() { when(properties.getEntityId()).thenReturn("https://antragsraum.ozgcloud.de/"); @@ -77,11 +80,19 @@ class BayernIdSamlConfigurationTest { } @Test - void shouldLoadVerificationCert() throws Exception { + void shouldLoadVerificationCertBayernId() { when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("bayernid-idp-infra.xml"))); bayernIdSamlConfiguration.initOpenSAML(); assertThat(bayernIdSamlConfiguration.getCertificatesFromMetadata()).isNotNull(); } + + @Test + void shouldLoadVerificationCertKeycloak() { + when(properties.getMetadataUri()).thenReturn(new InputStreamResource(TestUtils.loadFile("keycloak-idp.xml"))); + bayernIdSamlConfiguration.initOpenSAML(); + + assertThat(bayernIdSamlConfiguration.getCertificatesFromMetadata()).isNotNull(); + } } } \ No newline at end of file diff --git a/nachrichten-manager-server/src/test/resources/keycloak-idp.xml b/nachrichten-manager-server/src/test/resources/keycloak-idp.xml new file mode 100644 index 0000000000000000000000000000000000000000..e2a35a4c2c917e9ca25a86ae08f6aad494c9e2cd --- /dev/null +++ b/nachrichten-manager-server/src/test/resources/keycloak-idp.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (c) 2024. Das Land Schleswig-Holstein + ~ vertreten durch den Ministerpräsidenten des Landes Schleswig-Holstein + ~ Staatskanzlei Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + ~ + ~ Lizenziert unter der EUPL, Version 1.2 oder - sobald + ~ diese von der Europäischen Kommission genehmigt wurden - + ~ Folgeversionen der EUPL ("Lizenz"); + ~ Sie dürfen dieses Werk ausschließlich gemäß + ~ dieser Lizenz nutzen. + ~ Eine Kopie der Lizenz finden Sie hier: + ~ + ~ https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + ~ + ~ Sofern nicht durch anwendbare Rechtsvorschriften + ~ gefordert oder in schriftlicher Form vereinbart, wird + ~ die unter der Lizenz verbreitete Software "so wie sie + ~ ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + ~ ausdrücklich oder stillschweigend - verbreitet. + ~ Die sprachspezifischen Genehmigungen und Beschränkungen + ~ unter der Lizenz sind dem Lizenztext zu entnehmen. + --> + +<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:oasis:names:tc:SAML:2.0:metadata" + entityID="https://infra-pre-id.bayernportal.de/idp"> + <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <md:KeyDescriptor use="signing"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <ds:X509Data> + <ds:X509Certificate>MIIFbzCCA1egAwIBAgIJAPdFXXarkBN2MA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV + BAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYD + VQQKDARBS0RCMQwwCgYDVQQLDANJRE0wHhcNMjAxMDI3MTMxODQxWhcNMjUxMDI2 + MTMxODQxWjBOMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQH + DAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwDSURNMIICIjANBgkq + hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzDtWAEdC3J9FD+ti1exRhN1lzNgKWqO2 + gQNdJvlt7KGHA2VGGO7tqRogTuoqi/ydtiHJ8+lhp4kcWqyfv7i9HXOncvcsRRmR + dZjUY2Iui6ozJqD5LVm/vP5YfdP7vQPdbqyyfpoJhf3mbMEtdNDdGRnGIPUfDn+C + Fbo37f9tPwMgf3jgh4gxaujtLIhhr9gevVTEeZAFu9EvzLNd3kEtRb7MuXqIOdu1 + rW8HlGYFwwVLqEyBn8XG0QAIfhMmGjFMG7z+Kco2quwOmmZVzWQfeH/3AlN2KbcP + t7j+pl+6Bew2AAivP7O+95YKORqQjTu3rPWMF4txPId37MSjoytwBRyd5EACTvhQ + BOGrDFKQUOx6fTtRc8+7XGVz8MdQaZQWQXXh1ByU783twNdnRSrSVIyLdjiy1uCb + jvsSAtbzGBygPIvDo3skCNLNFXsChtHIfFFDK20KPGb0ghEDf2q3hDbFG3ZDGGyn + ZmJcZKuZhJqodJ/++sAXADyTJNAPVYDjKCF4ypELp2Eu/p1gaQPJEb74L/ZFZVOE + JFyXIiaqB9J+fcn/biqHHOmcCi8n9aIiNt1fatr1Z4lQRWoGtKaGU0+bzUSH4Bgs + 2EG4u1CI2MKDWqK2aEsHrtu8tbS9LrUmDVKtaEUOeul8xWVa036vp/YUIdiJNZSx + ZG4iTmSOATECAwEAAaNQME4wHQYDVR0OBBYEFFYeltslkaolOmcINXQeSe7nURwp + MB8GA1UdIwQYMBaAFFYeltslkaolOmcINXQeSe7nURwpMAwGA1UdEwQFMAMBAf8w + DQYJKoZIhvcNAQELBQADggIBAKqAlXoO41SAiycYUOrR90pfwTCysmbtHF5RWSCM + jF2aCG8URJ7bXwC0lBH8E5zCetFZwdqZziQtxzRkIOfhS5uWbH0RDhwuxZG+5RTP + yaHPAZI6e5xHDu8vHl/VbC3lnL/6K8l+Purr/yo8qkJqrPgThZRL9jBQyYRhDSsJ + UyIw5zcKKUQC/JWtMQAQcopbjekCs6xDT1HqIN90Sc/gOfYjNo0dGMNmro9mxcw8 + 2Iow18KNVdtEexfD+/6x4NPD61pzuQEe09TR+Cv3XyzBoGQ/2arijcPnGvth79ff + VFtRSf3fSs7wEKV9g3mEWXFDtPBhDj6K0kKU/kJfEZixkXl92MY+bmugrtTIrazj + tfrgMglIAHu9XCYWd/gef0J+PNfHsxgbTEr3XSC+5/xoFKPQSw3PgV8lkUDq4mJU + Ky/q4YmA37XQxourFR5pWvF03YACdtq6zPjtVeI7Cvkte6k0YW5S3cx9RmPv6YZh + laZ5ERpWNiv6IjokLsvNeemf2PApjO7Q2EDBIoHBYH31wwJSsyRDrSVmbaqLFI15 + fLXeh2A4YbaBDZdGvDiLOAk+dG1wdZ2aGw/uNBzMtc8VeKqI1HPcqIluBA3uUPpy + LLA+9hDPf6Pp4j0gkXxBikz+/h22bFxE1HmDiOSkEn+2NmOHuEFeA+D8jsCAL5VJ + 3emK + </ds:X509Certificate> + </ds:X509Data> + </ds:KeyInfo> + </md:KeyDescriptor> + <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> + <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/POST/SSO"/> + <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + Location="https://infra-pre-id.bayernportal.de/idp/profile/SAML2/Redirect/SSO"/> + </md:IDPSSODescriptor> +</md:EntityDescriptor> \ No newline at end of file