From a47ee1e6f55c26a3ed0f6d1d592ecf48ebafaa2b Mon Sep 17 00:00:00 2001 From: OZGCloud <ozgcloud@mgm-tp.com> Date: Tue, 3 Dec 2024 11:08:11 +0100 Subject: [PATCH] OZG-7092 add tests for configuration; remove outdated tests --- .../token/saml/SamlConfiguration.java | 13 +- .../token/TokenCheckApplicationTest.java | 37 -- .../saml/Saml2DecryptionServiceTest.java | 293 ------------- .../token/saml/Saml2ParseServiceTest.java | 246 ----------- .../saml/Saml2VerificationServiceTest.java | 384 ------------------ .../token/saml/SamlConfigurationTest.java | 182 +++++++++ .../token/saml/SamlTokenUtilsTest.java | 319 --------------- .../saml/SamlTokenValidationServiceTest.java | 3 +- 8 files changed, 190 insertions(+), 1287 deletions(-) delete mode 100644 token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckApplicationTest.java delete mode 100644 token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2DecryptionServiceTest.java delete mode 100644 token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2ParseServiceTest.java delete mode 100644 token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2VerificationServiceTest.java create mode 100644 token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlConfigurationTest.java delete mode 100644 token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenUtilsTest.java diff --git a/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlConfiguration.java b/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlConfiguration.java index b0148c8..a60ef4a 100644 --- a/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlConfiguration.java +++ b/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlConfiguration.java @@ -28,18 +28,13 @@ public class SamlConfiguration { private final SamlTrustEngineFactory samlTrustEngineFactory; private final SamlDecrypterFactory samlDecrypterFactory; - @Bean - SAMLSignatureProfileValidator samlSignatureProfileValidator() { - return new SAMLSignatureProfileValidator(); - } - @Bean ResponseUnmarshaller responseUnmarshaller() { return (ResponseUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory().getUnmarshaller(Response.DEFAULT_ELEMENT_NAME); } @Bean - SamlServiceRegistry samSettingsRegistry(TokenValidationProperties tokenValidationProperties) { + SamlServiceRegistry samServiceRegistry(TokenValidationProperties tokenValidationProperties) { var registryBuilder = SamlServiceRegistry.builder(); for (var tokenEntity : tokenValidationProperties.getEntities()) { registryBuilder.samlService(tokenEntity.getIdpEntityId(), samlTokenService(tokenEntity)); @@ -51,10 +46,16 @@ public class SamlConfiguration { return SamlTokenValidationService.builder() .signatureTrustEngine(samlTrustEngineFactory.buildSamlTrustEngine(tokenValidationProperty)) .decrypter(samlDecrypterFactory.buildDecrypter(tokenValidationProperty)) + .profileValidator(samlSignatureProfileValidator()) + .tokenValidationProperty(tokenValidationProperty) .verificationCriteria(buildVerificationCriteria(tokenValidationProperty.getIdpEntityId())) .build(); } + SAMLSignatureProfileValidator samlSignatureProfileValidator() { + return new SAMLSignatureProfileValidator(); + } + CriteriaSet buildVerificationCriteria(String idpEntityId) { return Stream.of( new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(idpEntityId)), diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckApplicationTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckApplicationTest.java deleted file mode 100644 index e664f6c..0000000 --- a/token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckApplicationTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2024. - * 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. - */ - -package de.ozgcloud.token; - -import org.junit.jupiter.api.Test; - -import net.shibboleth.utilities.java.support.component.ComponentInitializationException; - -class TokenCheckApplicationTest { - - @Test - void shouldCreateParserPool() throws ComponentInitializationException { - TokenCheckApplication application = new TokenCheckApplication(); - -// var parserPool = application.parserPool(); - -// assertThat(parserPool).isNotNull(); - } -} \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2DecryptionServiceTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2DecryptionServiceTest.java deleted file mode 100644 index 06d6514..0000000 --- a/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2DecryptionServiceTest.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2024. - * 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. - */ - -package de.ozgcloud.token.saml; - -import static de.ozgcloud.token.saml.SamlTokenTestUtils.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.Spy; -import org.opensaml.core.xml.XMLObject; -import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; -import org.opensaml.core.xml.schema.XSAny; -import org.opensaml.core.xml.schema.XSBoolean; -import org.opensaml.core.xml.schema.XSBooleanValue; -import org.opensaml.core.xml.schema.XSInteger; -import org.opensaml.core.xml.schema.XSString; -import org.opensaml.saml.saml2.core.Assertion; -import org.opensaml.saml.saml2.core.Attribute; -import org.opensaml.saml.saml2.core.AttributeStatement; -import org.opensaml.saml.saml2.core.AuthnStatement; -import org.opensaml.saml.saml2.core.Response; -import org.opensaml.saml.saml2.core.impl.AssertionBuilder; -import org.opensaml.saml.saml2.core.impl.AttributeBuilder; -import org.opensaml.saml.saml2.core.impl.AttributeStatementBuilder; -import org.opensaml.saml.saml2.core.impl.AuthnStatementBuilder; -import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller; -import org.opensaml.saml.saml2.encryption.Decrypter; -import org.springframework.security.saml2.Saml2Exception; - -import de.ozgcloud.common.test.TestUtils; -import de.ozgcloud.token.TokenAttributeTestFactory; -import net.shibboleth.utilities.java.support.component.ComponentInitializationException; - -class Saml2DecryptionServiceTest { - private Response samlResponse; - - @Spy - private Saml2DecryptionService service; - - private SamlSetting samlSetting; - private Saml2ParseService parseService; - - @BeforeEach - void setup() throws ComponentInitializationException { - var parserPool = SamlTokenTestUtils.initParserPool(); - - var samlSettingsRegistry = SamlTokenTestUtils.initConfig(BAYERN_ID); - samlSetting = samlSettingsRegistry.getService(IDP_ENTITY_ID_BAYERN_ID); - var responseUnmarshaller = (ResponseUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory() - .getUnmarshaller(Response.DEFAULT_ELEMENT_NAME); - parseService = new Saml2ParseService(parserPool, responseUnmarshaller); - var token = TestUtils.loadTextFile("SamlResponseBayernId.xml"); - samlResponse = parseService.parse(token); - } - - @Nested - class TestDecryptAttributes { - - @Test - void shouldCallDecryptResponseElements() { - service.decryptAttributes(samlResponse, samlSetting); - - verify(service).decryptResponseElements(samlResponse, samlSetting.getDecrypter()); - } - - @Test - void shouldCallGetAttributes() { - service.decryptAttributes(samlResponse, samlSetting); - - verify(service).getAttributes(samlResponse); - } - - @Test - void shouldReturnTokenAttributes() { - var tokenAttribute = TokenAttributeTestFactory.create(); - doReturn(List.of(tokenAttribute)).when(service).getAttributes(samlResponse); - - var res = service.decryptAttributes(samlResponse, samlSetting); - - assertThat(res).containsExactly(tokenAttribute); - } - } - - @Nested - class TestDecryptResponseElements { - - private Assertion assertion = new AssertionBuilder().buildObject(); - - @Test - void shouldCallDecryptAssertions() { - service.decryptResponseElements(samlResponse, samlSetting.getDecrypter()); - - samlResponse.getEncryptedAssertions() - .forEach(encryptedAssertion -> verify(service).decryptAssertion(encryptedAssertion, samlSetting.getDecrypter())); - } - - @Test - void shouldAddDecryptedAssertions() { - doReturn(assertion).when(service).decryptAssertion(any(), any()); - - service.decryptResponseElements(samlResponse, samlSetting.getDecrypter()); - - assertThat(samlResponse.getAssertions()).containsExactly(assertion); - } - - } - - @Nested - class TestDecryptAssertion { - - private Decrypter decrypter; - - @BeforeEach - void setUp() { - decrypter = samlSetting.getDecrypter(); - } - - @Nested - class OnDecryptionException { - @Test - void shouldThrowExceptionWhenDecryptionFails() { - var token = TestUtils.loadTextFile("BrokenSamlResponse.xml"); - var encryptedAssertion = parseService.parse(token).getEncryptedAssertions().getFirst(); - - assertThatExceptionOfType(Saml2Exception.class).isThrownBy( - () -> service.decryptAssertion(encryptedAssertion, decrypter)); - } - } - - @Nested - class OnNoException { - - @Test - void shouldHaveSubject() { - var samlAssertion = decryptAssertion(); - - assertThat(samlAssertion.getSubject()).isNotNull(); - } - - @Test - void shouldHaveAuthnStatements() { - var samlAssertion = decryptAssertion(); - - assertThat(samlAssertion.getAuthnStatements()).isNotNull(); - } - - @Test - void shouldHaveStatements() { - var samlAssertion = decryptAssertion(); - - assertThat(samlAssertion.getStatements()).isNotNull(); - } - - @Test - void shouldHaveAttributes() { - var samlAssertion = decryptAssertion(); - - var statements = (AttributeStatement) samlAssertion.getStatements().get(1); - var attributes = statements.getAttributes(); - assertThat(attributes).hasSize(7); - } - - private Assertion decryptAssertion() { - return service.decryptAssertion(samlResponse.getEncryptedAssertions().getFirst(), decrypter); - } - } - } - - @Nested - class TestGetAttributes { - private Attribute attribute = new AttributeBuilder().buildObject(); - private AuthnStatement authnStatement = new AuthnStatementBuilder().buildObject(); - private AttributeStatement attributeStatement = new AttributeStatementBuilder().buildObject(); - private Assertion assertion = new AssertionBuilder().buildObject(); - - @BeforeEach - void setUpData() { - attribute.setName(TokenAttributeTestFactory.NAME); - attributeStatement.getAttributes().add(attribute); - assertion.getStatements().addAll(List.of(authnStatement, attributeStatement)); - samlResponse.getAssertions().add(assertion); - } - - @Test - void shouldCallGetAttributeValues() { - service.getAttributes(samlResponse); - - verify(service).getAttributeValues(attribute); - } - - @Test - void shouldReturnAttributes() { - doReturn(TokenAttributeTestFactory.VALUE).when(service).getAttributeValues(attribute); - - var attributes = service.getAttributes(samlResponse); - - assertThat(attributes).containsExactly(TokenAttributeTestFactory.create()); - } - } - - @Nested - class TestGetAttributeValue { - - @Test - void shouldGetXSStringAttribute() { - var attributeValue = mock(XSString.class); - when(attributeValue.getValue()).thenReturn("test"); - - var value = service.getAttributeValue(attributeValue); - - assertThat(value).isEqualTo("test"); - } - - @Test - void shouldGetXSAnyAttribute() { - var attributeValue = mock(XSAny.class); - when(attributeValue.getTextContent()).thenReturn("test"); - - var value = service.getAttributeValue(attributeValue); - - assertThat(value).isEqualTo("test"); - } - - @Test - void shouldGetXSIntegerAttribute() { - var attributeValue = mock(XSInteger.class); - when(attributeValue.getValue()).thenReturn(1); - - var value = service.getAttributeValue(attributeValue); - - assertThat(value).isEqualTo("1"); - } - - @Test - void shouldGetXSBooleanAttribute() { - var attributeValue = mock(XSBoolean.class); - when(attributeValue.getValue()).thenReturn(XSBooleanValue.valueOf("true")); - - var value = service.getAttributeValue(attributeValue); - - assertThat(value).isEqualTo("true"); - } - - @Test - void shouldGetUnknownAttribute() { - var value = service.getAttributeValue(mock(XMLObject.class)); - - assertThat(value).startsWith("Mock for XMLObject"); - } - } - - @Nested - class TestGetAttributeValues { - @Test - void shouldGetAttributes() { - var attribute = mock(Attribute.class); - - var attributeValue1 = mock(XSInteger.class); - when(attributeValue1.getValue()).thenReturn(1); - var attributeValue2 = mock(XSString.class); - when(attributeValue2.getValue()).thenReturn("test"); - when(attribute.getAttributeValues()).thenReturn(List.of(attributeValue1, attributeValue2)); - - var value = service.getAttributeValues(attribute); - - assertThat(value).isEqualTo("1;test"); - } - } -} \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2ParseServiceTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2ParseServiceTest.java deleted file mode 100644 index 590504e..0000000 --- a/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2ParseServiceTest.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2024. - * 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. - */ - -package de.ozgcloud.token.saml; - -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedConstruction; -import org.mockito.Spy; -import org.opensaml.core.xml.XMLObject; -import org.opensaml.core.xml.io.UnmarshallingException; -import org.opensaml.saml.saml2.core.Response; -import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller; -import org.springframework.security.saml2.Saml2Exception; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import com.thedeanda.lorem.LoremIpsum; - -import de.ozgcloud.common.errorhandling.TechnicalException; -import lombok.SneakyThrows; -import net.shibboleth.utilities.java.support.xml.ParserPool; -import net.shibboleth.utilities.java.support.xml.XMLParserException; - -class Saml2ParseServiceTest { - - @InjectMocks - @Spy - private Saml2ParseService parser; - - @Mock - private ParserPool parserPool; - @Mock - private ResponseUnmarshaller unmarshaller; - - @Nested - class TestParse { - - private final String request = LoremIpsum.getInstance().getParagraphs(1, 1); - private final byte[] requestBytes = request.getBytes(StandardCharsets.UTF_8); - - @Mock - private Response response; - - @Nested - class OnNoException { - @BeforeEach - @SneakyThrows - void mock() { - doReturn(response).when(parser).createXmlObject(requestBytes); - } - - @Test - @SneakyThrows - void shouldCallCreateXmlObject() { - parse(); - - verify(parser).createXmlObject(requestBytes); - } - - @Test - void shouldReturnResponse() { - var returnedResponse = parse(); - - assertThat(returnedResponse).isEqualTo(response); - } - } - - @Nested - class OnIOException { - - @Test - @SneakyThrows - void shouldThrowTechnicalException() { - doThrow(IOException.class).when(parser).createXmlObject(requestBytes); - - assertThrows(TechnicalException.class, () -> parse()); - } - } - - Response parse() { - return parser.parse(request); - } - } - - @Nested - class TestCreateXmlObject { - - private final byte[] requestBytes = LoremIpsum.getInstance().getParagraphs(1, 1).getBytes(StandardCharsets.UTF_8); - - @Nested - class OnNoExceptions { - private MockedConstruction<ByteArrayInputStream> inputStreamConstruction; - private byte[] passedBytes; - private ByteArrayInputStream inputStream; - - @Mock - private Document document; - @Mock - private Element element; - @Mock - private XMLObject unmarshalledObject; - - @BeforeEach - @SneakyThrows - void setUp() { - inputStreamConstruction = mockConstruction(ByteArrayInputStream.class, - (inputStream, context) -> { - passedBytes = (byte[]) context.arguments().getFirst(); - this.inputStream = inputStream; - }); - when(parserPool.parse(any(InputStream.class))).thenReturn(document); - when(document.getDocumentElement()).thenReturn(element); - } - - @AfterEach - void cleanUp() { - inputStreamConstruction.close(); - } - - @Test - void shouldConstructByteArrayInputStream() { - createXmlObject(); - - assertThat(inputStreamConstruction.constructed()).hasSize(1); - } - - @Test - void shouldPassRequestToByteArrayInputStream() { - createXmlObject(); - - assertThat(passedBytes).isEqualTo(requestBytes); - } - - @Test - @SneakyThrows - void shouldCallParserPool() { - createXmlObject(); - - verify(parserPool).parse(inputStream); - } - - @Test - void shouldGetDocumentElement() { - createXmlObject(); - - verify(document).getDocumentElement(); - } - - @Test - @SneakyThrows - void shouldCallUnmarshaller() { - createXmlObject(); - - verify(unmarshaller).unmarshall(element); - } - - @Test - @SneakyThrows - void shouldReturnUnmarshalledObject() { - when(unmarshaller.unmarshall(element)).thenReturn(unmarshalledObject); - - var result = createXmlObject(); - - assertThat(result).isEqualTo(unmarshalledObject); - } - - @Test - @SneakyThrows - void shouldCloseInputStream() { - createXmlObject(); - - verify(inputStream).close(); - } - } - - @Nested - class OnXMLParserException { - - @Test - @SneakyThrows - void shouldThrowSaml2Exception() { - when(parserPool.parse(any(InputStream.class))).thenThrow(XMLParserException.class); - - assertThrows(Saml2Exception.class, () -> createXmlObject()); - } - } - - @Nested - class OnUnmarshallingException { - - @Mock - private Document document; - - @BeforeEach - @SneakyThrows - void setUp() { - when(parserPool.parse(any(InputStream.class))).thenReturn(document); - } - - @Test - @SneakyThrows - void shouldThrowSaml2Exception() { - when(unmarshaller.unmarshall(any())).thenThrow(UnmarshallingException.class); - - assertThrows(Saml2Exception.class, () -> createXmlObject()); - } - } - - @SneakyThrows - private XMLObject createXmlObject() { - return parser.createXmlObject(requestBytes); - } - } -} \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2VerificationServiceTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2VerificationServiceTest.java deleted file mode 100644 index f9d3a4e..0000000 --- a/token-checker-server/src/test/java/de/ozgcloud/token/saml/Saml2VerificationServiceTest.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2024. - * 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. - */ - -package de.ozgcloud.token.saml; - -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.opensaml.saml.saml2.core.Issuer; -import org.opensaml.saml.saml2.core.Response; -import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; -import org.opensaml.security.SecurityException; -import org.opensaml.xmlsec.signature.Signature; -import org.opensaml.xmlsec.signature.support.SignatureException; -import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; -import org.springframework.security.saml2.core.Saml2Error; -import org.springframework.security.saml2.core.Saml2ErrorCodes; - -import com.thedeanda.lorem.LoremIpsum; - -import de.ozgcloud.token.common.errorhandling.TokenVerificationExceptionTestFactory; -import lombok.SneakyThrows; -import net.shibboleth.utilities.java.support.resolver.CriteriaSet; - -class Saml2VerificationServiceTest { - @InjectMocks - @Spy - private Saml2VerificationService verifier; - - @Mock - private Saml2ParseService parser; - @Mock - private SamlServiceRegistry samlServiceRegistry; - @Mock - private SAMLSignatureProfileValidator profileValidator; - - @Nested - class TestVerify { - private final String token = LoremIpsum.getInstance().getParagraphs(1, 1); - @Mock - private Response response; - - @Test - void shouldParseToken() { - doReturn(Collections.emptyList()).when(verifier).getSaml2Errors(any()); - - callVerify(); - - verify(parser).parse(token); - } - - @Test - void shouldCallGetSaml2Errors() { - when(parser.parse(token)).thenReturn(response); - - callVerify(); - - verify(verifier).getSaml2Errors(response); - } - - @Test - void shouldReturnSaml2Errors() { - var saml2Error = TokenVerificationExceptionTestFactory.createSaml2Error(); - doReturn(List.of(saml2Error)).when(verifier).getSaml2Errors(any()); - - var errors = callVerify(); - - assertThat(errors).containsExactly(saml2Error); - } - - private List<Saml2Error> callVerify() { - return verifier.verify(token); - } - } - - @Nested - class TestGetSaml2Errors { - - @Mock - private Response response; - - @Test - void shouldCheckIsSigned() { - getSaml2Errors(); - - verify(response).isSigned(); - } - - @Nested - class OnIsSigned { - - private final Saml2Error profileError = new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, "Profile Error"); - private final Saml2Error signatureError = new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, "Signature Error"); - - @SuppressWarnings("unchecked") - @BeforeEach - void givenIsSigned() { - when(response.isSigned()).thenReturn(true); - doAnswer(invocation -> ((List<Saml2Error>) invocation.getArgument(1)).add(profileError)) - .when(verifier).validateProfile(any(), any()); - doAnswer(invocation -> ((List<Saml2Error>) invocation.getArgument(1)).add(signatureError)) - .when(verifier).validateSignature(any(), any()); - } - - @Test - void shouldCallValidateProfile() { - getSaml2Errors(); - - verify(verifier).validateProfile(eq(response), anyList()); - } - - @Test - void shouldCallValidateSignature() { - getSaml2Errors(); - - verify(verifier).validateSignature(eq(response), anyList()); - } - - @Test - void shouldReturnSaml2Errors() { - var errors = getSaml2Errors(); - - assertThat(errors).containsExactlyInAnyOrder(profileError, signatureError); - } - } - - @Nested - class OnIsNotSigned { - - @BeforeEach - void givenIsNotSigned() { - when(response.isSigned()).thenReturn(false); - } - - @Test - void shouldNotCallValidateProfile() { - getSaml2Errors(); - - verify(verifier, never()).validateProfile(any(), any()); - } - - @Test - void shouldNotCallValidateSignature() { - getSaml2Errors(); - - verify(verifier, never()).validateSignature(any(), any()); - } - - @Test - void shouldReturnSaml2Error() { - var expectedSaml2Error = new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, Saml2VerificationService.SIGNATURE_MISSING); - - var errors = getSaml2Errors(); - - assertThat(errors).usingRecursiveFieldByFieldElementComparator().containsExactly(expectedSaml2Error); - } - - @Test - void shouldReturnUnmodifiableList() { - var saml2Error = TokenVerificationExceptionTestFactory.createSaml2Error(); - - var list = getSaml2Errors(); - - assertThrows(UnsupportedOperationException.class, () -> list.add(saml2Error)); - } - } - - private List<Saml2Error> getSaml2Errors() { - return verifier.getSaml2Errors(response); - } - } - - @Nested - class TestValidateProfile { - - @Mock - private Response response; - @Mock - private Signature signature; - - private List<Saml2Error> errors; - - @BeforeEach - void setUp() { - when(response.getSignature()).thenReturn(signature); - errors = new ArrayList<>(); - } - - @Test - @SneakyThrows - void shouldCallProfileValidator() { - validateProfile(); - - verify(profileValidator).validate(signature); - } - - @Nested - class OnSignatureException { - - @Test - @SneakyThrows - void shouldAddSaml2Error() { - var expectedSaml2Error = new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, - Saml2VerificationService.INVALID_SIGNATURE_PROFILE + Saml2VerificationService.FORMAT.formatted(response.getID())); - doThrow(SignatureException.class).when(profileValidator).validate(signature); - - validateProfile(); - - assertThat(errors).usingRecursiveFieldByFieldElementComparator().containsExactly(expectedSaml2Error); - } - } - - @Nested - class OnNoSignatureException { - - @Test - @SneakyThrows - void shouldNotAddSaml2Error() { - validateProfile(); - - assertThat(errors).isEmpty(); - } - } - - private void validateProfile() { - verifier.validateProfile(response, errors); - } - } - - @Nested - class TestValidateSignature { - - @Mock - private Response response; - @Mock - private Signature signature; - @Mock - private Issuer issuer; - private final String issuerValue = LoremIpsum.getInstance().getWords(1); - - @Mock - private SamlSetting samlSetting; - @Mock - private SignatureTrustEngine trustEngine; - @Mock - private CriteriaSet criteriaSet; - - private List<Saml2Error> errors; - private final String id = UUID.randomUUID().toString(); - - @BeforeEach - void setUp() { - when(response.getSignature()).thenReturn(signature); - when(response.getIssuer()).thenReturn(issuer); - when(issuer.getValue()).thenReturn(issuerValue); - errors = new ArrayList<>(); - when(samlServiceRegistry.getService(any())).thenReturn(samlSetting); - when(samlSetting.getTrustEngine()).thenReturn(trustEngine); - when(samlSetting.getCriteriaSet()).thenReturn(criteriaSet); - } - - @Test - void shouldGetSamlSetting() { - validateSignature(); - - verify(samlServiceRegistry).getService(issuerValue); - } - - @Test - void shouldGetTrustEngine() { - validateSignature(); - - verify(samlSetting).getTrustEngine(); - } - - @Test - void shouldGetCriteriaSet() { - validateSignature(); - - verify(samlSetting).getCriteriaSet(); - } - - @Test - @SneakyThrows - void shouldCallTrustEngine() { - validateSignature(); - - verify(trustEngine).validate(signature, criteriaSet); - } - - @Nested - class OnInvalidSignature { - - private final Saml2Error invalidSignatureError = new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, - Saml2VerificationService.INVALID_SIGNATURE + Saml2VerificationService.FORMAT.formatted(id)); - - @BeforeEach - @SneakyThrows - void givenSignatureIsInvalid() { - when(trustEngine.validate(any(), any())).thenReturn(false); - when(response.getID()).thenReturn(id); - } - - @Test - void shouldAddSaml2Error() { - validateSignature(); - - assertThat(errors).usingRecursiveFieldByFieldElementComparator().containsExactly(invalidSignatureError); - } - } - - @Nested - class OnValidSignature { - - @BeforeEach - @SneakyThrows - void givenSignatureIsValid() { - when(trustEngine.validate(any(), any())).thenReturn(true); - } - - @Test - void shouldNotAddSaml2Error() { - validateSignature(); - - assertThat(errors).isEmpty(); - } - } - - @Nested - class OnSecurityException { - - private final Saml2Error errorOnValidatingSignatureError = new Saml2Error(Saml2ErrorCodes.INVALID_SIGNATURE, - Saml2VerificationService.ERROR_VALIDATING_SIGNATURE + Saml2VerificationService.FORMAT.formatted(id)); - - @BeforeEach - @SneakyThrows - void givenSecurityExceptionThrown() { - when(trustEngine.validate(any(), any())).thenThrow(SecurityException.class); - when(response.getID()).thenReturn(id); - } - - @Test - void shouldAddSaml2Error() { - validateSignature(); - - assertThat(errors).usingRecursiveFieldByFieldElementComparator().containsExactly(errorOnValidatingSignatureError); - } - } - - private void validateSignature() { - verifier.validateSignature(response, errors); - } - } -} \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlConfigurationTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlConfigurationTest.java new file mode 100644 index 0000000..82f6021 --- /dev/null +++ b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlConfigurationTest.java @@ -0,0 +1,182 @@ +/* + * 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. + */ +package de.ozgcloud.token.saml; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.opensaml.saml.saml2.encryption.Decrypter; +import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; +import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.token.TokenValidationProperties; +import de.ozgcloud.token.TokenValidationProperties.TokenValidationProperty; +import net.shibboleth.utilities.java.support.resolver.CriteriaSet; + +class SamlConfigurationTest { + + private static final String IDP_ENTITY_ID = LoremIpsum.getInstance().getWords(1); + + @Spy + @InjectMocks + private SamlConfiguration configuration; + + @Mock + private SamlTrustEngineFactory samlTrustEngineFactory; + @Mock + private SamlDecrypterFactory samlDecrypterFactory; + + @Nested + class TestSamlServiceRegistry { + + @Mock + private TokenValidationProperties tokenValidationProperties; + @Mock + private TokenValidationProperty tokenValidationProperty; + @Mock + private SamlTokenValidationService tokenValidationService; + + @BeforeEach + void init() { + when(tokenValidationProperty.getIdpEntityId()).thenReturn(IDP_ENTITY_ID); + when(tokenValidationProperties.getEntities()).thenReturn(List.of(tokenValidationProperty)); + doReturn(tokenValidationService).when(configuration).samlTokenService(any()); + } + + @Test + void shouldCallSamlTokenService() { + configuration.samServiceRegistry(tokenValidationProperties); + + verify(configuration).samlTokenService(tokenValidationProperty); + } + + @Test + void shouldAddService() { + var result = configuration.samServiceRegistry(tokenValidationProperties); + + assertThat(result.getService(IDP_ENTITY_ID)).contains(tokenValidationService); + } + } + + @Nested + class TestSamlTokenService { + + @Mock + private TokenValidationProperty tokenValidationProperty; + @Mock + private SignatureTrustEngine signatureTrustEngine; + @Mock + private Decrypter decrypter; + @Mock + private SAMLSignatureProfileValidator profileValidator; + @Mock + private CriteriaSet verificationCriteria; + + @BeforeEach + void init() { + when(tokenValidationProperty.getIdpEntityId()).thenReturn(IDP_ENTITY_ID); + doReturn(signatureTrustEngine).when(samlTrustEngineFactory).buildSamlTrustEngine(any()); + doReturn(decrypter).when(samlDecrypterFactory).buildDecrypter(any()); + doReturn(profileValidator).when(configuration).samlSignatureProfileValidator(); + doReturn(verificationCriteria).when(configuration).buildVerificationCriteria(any()); + } + + @Test + void shouldCallBuildSamlTrustEngine() { + samlTokenService(); + + verify(samlTrustEngineFactory).buildSamlTrustEngine(tokenValidationProperty); + } + + @Test + void shouldSetSignatureTrustEngine() { + var result = samlTokenService(); + + assertThat(result).extracting("signatureTrustEngine").isEqualTo(signatureTrustEngine); + } + + @Test + void shouldCallBuildDecrypter() { + samlTokenService(); + + verify(samlDecrypterFactory).buildDecrypter(tokenValidationProperty); + } + + @Test + void shouldSetDecrypter() { + var result = samlTokenService(); + + assertThat(result).extracting("decrypter").isEqualTo(decrypter); + } + + @Test + void shouldCallSamlSignatureProfileValidator() { + samlTokenService(); + + verify(configuration).samlSignatureProfileValidator(); + } + + @Test + void shouldSetProfileValidator() { + var result = samlTokenService(); + + assertThat(result).extracting("profileValidator").isEqualTo(profileValidator); + } + + @Test + void shouldSetTokenValidationProperty() { + var result = samlTokenService(); + + assertThat(result).extracting("tokenValidationProperty").isEqualTo(tokenValidationProperty); + } + + @Test + void shouldCallBuildVerificationCriteria() { + samlTokenService(); + + verify(configuration).buildVerificationCriteria(IDP_ENTITY_ID); + } + + @Test + void shouldSetVerificationCriteria() { + var result = samlTokenService(); + + assertThat(result).extracting("verificationCriteria").isEqualTo(verificationCriteria); + } + + private SamlTokenValidationService samlTokenService() { + return configuration.samlTokenService(tokenValidationProperty); + } + } +} \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenUtilsTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenUtilsTest.java deleted file mode 100644 index 87e7b67..0000000 --- a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenUtilsTest.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2024. - * 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. - */ - -package de.ozgcloud.token.saml; - -import static de.ozgcloud.token.saml.SamlTokenUtils.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.List; - -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.opensaml.core.config.ConfigurationService; -import org.opensaml.core.config.InitializationService; -import org.opensaml.core.xml.XMLObject; -import org.opensaml.core.xml.config.XMLObjectProviderRegistry; -import org.opensaml.saml.saml2.metadata.EntityDescriptor; -import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; -import org.opensaml.saml.saml2.metadata.KeyDescriptor; -import org.opensaml.security.credential.UsageType; -import org.opensaml.xmlsec.signature.KeyInfo; -import org.opensaml.xmlsec.signature.X509Data; -import org.springframework.core.io.InputStreamResource; -import org.springframework.core.io.Resource; -import org.springframework.security.saml2.Saml2Exception; - -import de.ozgcloud.common.test.TestUtils; -import de.ozgcloud.token.TokenValidationProperties; -import net.shibboleth.utilities.java.support.component.ComponentInitializationException; -import net.shibboleth.utilities.java.support.xml.BasicParserPool; -import net.shibboleth.utilities.java.support.xml.ParserPool; -import net.shibboleth.utilities.java.support.xml.XMLParserException; - -class SamlTokenUtilsTest { - private XMLObjectProviderRegistry registry; - private ParserPool parserPool; - - @BeforeEach - void setup() throws ComponentInitializationException { - parserPool = parserPool(); - initOpenSAML(); - } - - private ParserPool parserPool() throws ComponentInitializationException { - var localParserPool = new BasicParserPool(); - - final var features = SamlTokenUtils.createFeatureMap(); - localParserPool.setBuilderFeatures(features); - localParserPool.setBuilderAttributes(new HashMap<>()); - localParserPool.initialize(); - - return localParserPool; - } - - public void initOpenSAML() { - try { - registry = new XMLObjectProviderRegistry(); - ConfigurationService.register(XMLObjectProviderRegistry.class, registry); - - registry.setParserPool(parserPool); - InitializationService.initialize(); - } catch (Exception e) { - throw new RuntimeException("Initialization failed"); - } - } - - XMLObject xmlObject(InputStream inputStream) throws XMLParserException { - var document = parserPool.parse(inputStream); - var element = document.getDocumentElement(); - var unmarshaller = registry.getUnmarshallerFactory().getUnmarshaller(element); - if (unmarshaller == null) { - throw new Saml2Exception("Unsupported element of type " + element.getTagName()); - } - try { - return unmarshaller.unmarshall(element); - } catch (Exception ex) { - throw new Saml2Exception(ex); - } - } - - @Nested - class TestXmlFeatureMapCreation { - @Test - void shouldCreateFeatureMap() { - var map = SamlTokenUtils.createFeatureMap(); - - assertThat(map).isNotNull(); - } - - @Test - void shouldHaveExternalGeneralEntitiesFalse() { - var map = SamlTokenUtils.createFeatureMap(); - - assertThat(map).containsEntry(FEATURES_EXTERNAL_GENERAL_ENTITIES, Boolean.FALSE); - } - - @Test - void shouldHaveExternalParameterEntitiesFalse() { - var map = SamlTokenUtils.createFeatureMap(); - - assertThat(map).containsEntry(FEATURES_EXTERNAL_PARAMETER_ENTITIES, Boolean.FALSE); - } - - @Test - void shouldHaveDisallowDocTypeDeclTrue() { - var map = SamlTokenUtils.createFeatureMap(); - - assertThat(map).containsEntry(FEATURES_DISALLOW_DOCTYPE_DECL, Boolean.TRUE); - } - - @Test - void shouldHaveValidationSchemaNormalizedFalse() { - var map = SamlTokenUtils.createFeatureMap(); - - assertThat(map).containsEntry(VALIDATION_SCHEMA_NORMALIZED_VALUE, Boolean.FALSE); - } - - @Test - void shouldHaveSecureProcessingTrue() { - var map = SamlTokenUtils.createFeatureMap(); - - assertThat(map).containsEntry(FEATURE_SECURE_PROCESSING, Boolean.TRUE); - } - } - - @Nested - class TestLoadingDecryptionCredentials { - private Resource key; - private Resource certificate; - - @BeforeEach - void setUp() { - TokenValidationProperties tokenCheckerProperties = SamlTokenTestUtils.initProperties(); - key = tokenCheckerProperties.getEntities().getFirst().getKey(); - certificate = tokenCheckerProperties.getEntities().getFirst().getCertificate(); - } - - @Test - void shouldLoadDecryptionCredentials() { - var credentials = SamlTokenUtils.getDecryptionCredential(key, certificate); - - assertThat(credentials).isNotNull(); - } - - @Test - void shouldNotLoadDecryptionCredentialsBecauseOfMissingCertificate() { - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> SamlTokenUtils.getDecryptionCredential(key, null)) - .withMessageStartingWith(NO_CERTIFICATE_LOCATION_SPECIFIED); - } - - @Test - void shouldNotLoadDecryptionCredentialsBecauseOfMissingKey() { - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> SamlTokenUtils.getDecryptionCredential(null, certificate)) - .withMessageStartingWith(NO_PRIVATE_KEY_LOCATION_SPECIFIED); - } - - @Nested - class TestMissingLocations { - @Mock - private Resource keyMock; - @Mock - private Resource certificateMock; - - @Test - void shouldNotLoadDecryptionCredentialsBecauseOfMissingCertificateLocation() { - when(certificateMock.exists()).thenReturn(Boolean.FALSE); - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> SamlTokenUtils.getDecryptionCredential(key, certificateMock)) - .withMessageStartingWith(CERTIFICATE_LOCATION); - } - - @Test - void shouldNotLoadDecryptionCredentialsBecauseOfCertificateIOException() throws IOException { - when(certificateMock.exists()).thenReturn(Boolean.TRUE); - when(certificateMock.getInputStream()).thenThrow(new IOException("test")); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> SamlTokenUtils.getDecryptionCredential(key, certificateMock)); - } - - @Test - void shouldNotLoadDecryptionCredentialsBecauseOfMissingKeyLocation() { - when(keyMock.exists()).thenReturn(Boolean.FALSE); - - assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - () -> SamlTokenUtils.getDecryptionCredential(keyMock, certificate)).withMessageStartingWith(PRIVATE_KEY_LOCATION); - } - - @Test - void shouldNotLoadDecryptionCredentialsBecauseOfKeyIOException() throws IOException { - when(keyMock.exists()).thenReturn(Boolean.TRUE); - when(keyMock.getInputStream()).thenThrow(new IOException("test")); - - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy( - () -> SamlTokenUtils.getDecryptionCredential(keyMock, certificate)); - } - } - } - - @Nested - class TestLoadingEntityDescriptor { - @Test - void shouldLoadEntityDescriptor() throws IOException, XMLParserException { - var metadata = xmlObject(new InputStreamResource(TestUtils.loadFile("metadata/bayernid-idp-infra.xml")).getInputStream()); - - var entityDescriptor = SamlTokenUtils.findEntityDescriptor(metadata); - - assertThat(entityDescriptor).isNotEmpty(); - } - - @Test - void shouldLoadEntitiesDescriptor() throws IOException, XMLParserException { - var metadata = xmlObject(new InputStreamResource(TestUtils.loadFile("metadata/mujina_metadata.xml")).getInputStream()); - - var entityDescriptor = SamlTokenUtils.findEntityDescriptor(metadata); - - assertThat(entityDescriptor).isNotEmpty(); - } - - @Test - void shouldNotLoadEntityDescriptor() { - var entityDescriptor = SamlTokenUtils.findEntityDescriptor(null); - - assertThat(entityDescriptor).isEmpty(); - } - - @Test - void shouldNotLoadEntityDescriptorMissing() throws IOException, XMLParserException { - var metadata = xmlObject(new InputStreamResource(TestUtils.loadFile("metadata/missing_IDPSSODescriptor.xml")).getInputStream()); - - var entityDescriptor = SamlTokenUtils.findEntityDescriptor(metadata); - - assertThat(entityDescriptor).isEmpty(); - } - } - - @Nested - class TestLoadingVerificationCertificatesFromIDPMetadata { - - @Test - void shouldLoadVerificationCertificates() throws IOException, XMLParserException { - var metadata = xmlObject(new InputStreamResource(TestUtils.loadFile("metadata/bayernid-idp-infra.xml")).getInputStream()); - - var credentials = SamlTokenUtils.getVerificationCertificates(SamlTokenUtils.findEntityDescriptor(metadata).get()); - - assertThat(credentials).isNotEmpty(); - } - - @Test - void shouldNotLoadVerificationCertificatesBecauseOfException() throws IOException, XMLParserException { - var metadata = xmlObject(new InputStreamResource(TestUtils.loadFile("metadata/invalid_IDPSSODescriptor.xml")).getInputStream()); - var cert = SamlTokenUtils.findEntityDescriptor(metadata).get(); - - assertThatExceptionOfType(Saml2Exception.class).isThrownBy( - () -> SamlTokenUtils.getVerificationCertificates(cert)) - .withMessageStartingWith(SAML_ASSERTIONS_VERIFICATION_EMPTY); - } - - @Test - void shouldNotLoadVerificationCertificatesBecauseMissing() { - var entityDescriptor = mock(EntityDescriptor.class); - when(entityDescriptor.getIDPSSODescriptor(anyString())).thenReturn(null); - - assertThatExceptionOfType(Saml2Exception.class).isThrownBy( - () -> SamlTokenUtils.getVerificationCertificates(entityDescriptor)) - .withMessageStartingWith(MISSING_THE_NECESSARY_IDPSSODESCRIPTOR_ELEMENT); - } - - @Test - void shouldNotLoadCertificates() { - var entityDescriptor = initTestData(); - - assertThatExceptionOfType(Saml2Exception.class).isThrownBy( - () -> SamlTokenUtils.getVerificationCertificates(entityDescriptor)); - } - - private static @NotNull EntityDescriptor initTestData() { - var entityDescriptor = mock(EntityDescriptor.class); - var iDPSSODescriptor = mock(IDPSSODescriptor.class); - var keyDescriptor = mock(KeyDescriptor.class); - var keyInfo = mock(KeyInfo.class); - var x509Data = mock(X509Data.class); - var x509Certificate = mock(org.opensaml.xmlsec.signature.X509Certificate.class); - when(x509Certificate.getValue()).thenReturn( - "-----BEGIN CERTIFICATE-----\nMIIEGzCCAwOgAwIBAgIUWPZFfhB4+iI3XdjUTMqhhDkljGgwDQYJKoZIhvcNAQEL\n-----END CERTIFICATE-----"); - when(x509Data.getX509Certificates()).thenReturn(List.of(x509Certificate)); - when(keyInfo.getX509Datas()).thenReturn(List.of(x509Data)); - when(keyDescriptor.getKeyInfo()).thenReturn(keyInfo); - when(keyDescriptor.getUse()).thenReturn(UsageType.SIGNING); - when(iDPSSODescriptor.getKeyDescriptors()).thenReturn(List.of(keyDescriptor)); - when(entityDescriptor.getIDPSSODescriptor(anyString())).thenReturn(iDPSSODescriptor); - return entityDescriptor; - } - } -} \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenValidationServiceTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenValidationServiceTest.java index 7f0fd52..7998a53 100644 --- a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenValidationServiceTest.java +++ b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTokenValidationServiceTest.java @@ -415,8 +415,7 @@ class SamlTokenValidationServiceTest { @Test void shouldReturnWhenXSBoolean() { var value = String.valueOf(new Random().nextBoolean()); - XSBooleanValue booleanValue = when(mock(XSBooleanValue.class).toString()).thenReturn(value).getMock(); - XMLObject attrValue = when(mock(XSBoolean.class).getValue()).thenReturn(booleanValue).getMock(); + XMLObject attrValue = when(mock(XSBoolean.class).getValue()).thenReturn(XSBooleanValue.valueOf(value)).getMock(); var result = service.getAttributeValue(attrValue); -- GitLab