diff --git a/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlAttributeService.java b/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlAttributeService.java index 3e17925bacea2d0c0d1e4fbe8fe698202759098b..0b2d44ee425e67c04024e2a1523a2d822c7545b7 100644 --- a/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlAttributeService.java +++ b/token-checker-server/src/main/java/de/ozgcloud/token/saml/SamlAttributeService.java @@ -151,27 +151,29 @@ public class SamlAttributeService { TokenAttributes buildTokenAttributes(Map<String, String> tokenAttributes, Response token) { var tokenAttributesBuilder = TokenAttributes.builder().postfachId(getPostfachId(tokenAttributes, token)) .trustLevel(getTrustLevel(tokenAttributes)); - tokenAttributes.entrySet().stream().filter(this::isNotNamedField).map(this::buildTokenAttribute) + tokenAttributes.entrySet().stream().filter(this::isNotNamedAttribute).map(this::buildTokenAttribute) .forEach(tokenAttributesBuilder::otherAttribute); return tokenAttributesBuilder.build(); } String getPostfachId(Map<String, String> tokenAttributes, Response token) { - return tokenValidationProperty.isUseIdAsPostfachId() ? token.getID() : getValue(tokenAttributes, TokenAttributes.POSTFACH_ID_KEY); + return tokenValidationProperty.isUseIdAsPostfachId() ? token.getID() : tokenAttributes.get(getPostfachIdKey()); } String getTrustLevel(Map<String, String> tokenAttributes) { - return getValue(tokenAttributes, TokenAttributes.TRUST_LEVEL_KEY); + return tokenAttributes.get(getTrustLevelKey()); } - String getValue(Map<String, String> tokenAttributes, String key) { - var mappedKey = tokenValidationProperty.getMappings().getOrDefault(key, key); - return tokenAttributes.get(mappedKey); + boolean isNotNamedAttribute(Map.Entry<String, String> attributeEntry) { + return !StringUtils.equalsAny(attributeEntry.getKey(), getPostfachIdKey(), getTrustLevelKey()); } - boolean isNotNamedField(Map.Entry<String, String> attributeEntry) { - var mappedKey = tokenValidationProperty.getMappings().get(attributeEntry.getKey()); - return !StringUtils.equalsAny(mappedKey, TokenAttributes.POSTFACH_ID_KEY, TokenAttributes.TRUST_LEVEL_KEY); + String getPostfachIdKey() { + return tokenValidationProperty.getMappings().getOrDefault(TokenAttributes.POSTFACH_ID_KEY, TokenAttributes.POSTFACH_ID_KEY); + } + + String getTrustLevelKey() { + return tokenValidationProperty.getMappings().getOrDefault(TokenAttributes.TRUST_LEVEL_KEY, TokenAttributes.TRUST_LEVEL_KEY); } TokenAttribute buildTokenAttribute(Map.Entry<String, String> attribute) { 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 b45b6df9a0e8a11d1ebc06fe833804425a169a48..9cdb801ebaa2e5e3752a5b44fcbd9d9582a97efe 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 @@ -63,12 +63,12 @@ public class SamlConfiguration { SamlServiceRegistry samlServiceRegistry(TokenValidationProperties tokenValidationProperties) { var registryBuilder = SamlServiceRegistry.builder(); for (var tokenEntity : tokenValidationProperties.getEntities()) { - registryBuilder.samlService(tokenEntity.getIdpEntityId(), samlTokenService(tokenEntity)); + registryBuilder.samlService(tokenEntity.getIdpEntityId(), samlAttributeService(tokenEntity)); } return registryBuilder.build(); } - SamlAttributeService samlTokenService(TokenValidationProperty tokenValidationProperty) { + SamlAttributeService samlAttributeService(TokenValidationProperty tokenValidationProperty) { return SamlAttributeService.builder() .signatureTrustEngine(samlTrustEngineFactory.buildSamlTrustEngine(tokenValidationProperty)) .decrypter(samlDecrypterFactory.buildDecrypter(tokenValidationProperty)) diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckGrpcServiceITCase.java b/token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckGrpcServiceITCase.java index 2aa772fb43a1bc08306f0085a3a93e95584f8935..8648a6a1feeb4fc2dc54194bce7295ac255e0f3d 100644 --- a/token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckGrpcServiceITCase.java +++ b/token-checker-server/src/test/java/de/ozgcloud/token/TokenCheckGrpcServiceITCase.java @@ -36,7 +36,7 @@ import org.springframework.test.annotation.DirtiesContext; import de.ozgcloud.common.test.ITCase; import de.ozgcloud.common.test.TestUtils; -import de.ozgcloud.token.saml.SamlTrustEngineFactoryTestConfiguration; +import de.ozgcloud.token.saml.SamlTestConfiguration; import net.devh.boot.grpc.client.inject.GrpcClient; public class TokenCheckGrpcServiceITCase { @@ -47,16 +47,13 @@ public class TokenCheckGrpcServiceITCase { "grpc.server.inProcessName=test", "grpc.client.token-checker.address=in-process:test", }) - @Import(SamlTrustEngineFactoryTestConfiguration.class) + @Import(SamlTestConfiguration.class) @DirtiesContext class TestCheckTokenSuccessfully { @GrpcClient("token-checker") private TokenCheckServiceGrpc.TokenCheckServiceBlockingStub tokenCheckerStub; - private static final String POSTFACH_ID = "28721c6f-b78f-4d5c-a048-19fd2fc429d2"; - private static final String TRUST_LEVEL = "STORK-QAA-Level-1"; - @Test void shouldAcceptToken() { var token = TestUtils.loadTextFile("SamlResponseBayernId.xml"); @@ -72,9 +69,9 @@ public class TokenCheckGrpcServiceITCase { var result = tokenCheckerStub.checkToken(buildCheckTokenRequest(token)); - assertThat(result.getTokenAttributes().getPostfachId()).isEqualTo(POSTFACH_ID); - assertThat(result.getTokenAttributes().getTrustLevel()).isEqualTo(TRUST_LEVEL); - assertThat(getOtherFields(result)).isNotEmpty().doesNotContainKeys(TokenAttributes.POSTFACH_ID_KEY, TokenAttributes.TRUST_LEVEL_KEY); + assertThat(result.getTokenAttributes().getPostfachId()).isEqualTo(SamlTestConfiguration.POSTFACH_ID); + assertThat(result.getTokenAttributes().getTrustLevel()).isEqualTo(SamlTestConfiguration.TRUST_LEVEL); + assertThat(getOtherFields(result)).hasSize(4).doesNotContainKeys(TokenAttributes.POSTFACH_ID_KEY, TokenAttributes.TRUST_LEVEL_KEY); } private Map<String, String> getOtherFields(GrpcCheckTokenResponse result) { diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlAttributeServiceTest.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlAttributeServiceTest.java index ab3d43919b34ba1bc234f2c96c97243bc8092360..c8cf4fd79b869a07e1304da08af6eac92e33c935 100644 --- a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlAttributeServiceTest.java +++ b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlAttributeServiceTest.java @@ -563,7 +563,7 @@ class SamlAttributeServiceTest { @BeforeEach void init() { - doReturn(true).when(service).isNotNamedField(any()); + doReturn(true).when(service).isNotNamedAttribute(any()); doReturn(TokenAttributesTestFactory.OTHER_ATTRIBUTE).when(service).buildTokenAttribute(any()); } @@ -603,7 +603,7 @@ class SamlAttributeServiceTest { void shouldCallIsNotMappedField() { buildTokenAttributes(); - verify(service).isNotNamedField(Map.entry(TokenAttributeTestFactory.NAME, TokenAttributeTestFactory.VALUE)); + verify(service).isNotNamedAttribute(Map.entry(TokenAttributeTestFactory.NAME, TokenAttributeTestFactory.VALUE)); } @Test @@ -663,14 +663,14 @@ class SamlAttributeServiceTest { @BeforeEach void init() { when(tokenValidationProperty.isUseIdAsPostfachId()).thenReturn(false); - doReturn(TokenAttributeTestFactory.VALUE).when(service).getValue(any(), any()); + doReturn(TokenAttributeTestFactory.NAME).when(service).getPostfachIdKey(); } @Test void shouldCallGetMappedValue() { getPostfachId(); - verify(service).getValue(TokenAttributeTestFactory.asMap(), TokenAttributes.POSTFACH_ID_KEY); + verify(service).getPostfachIdKey(); } @Test @@ -691,14 +691,14 @@ class SamlAttributeServiceTest { @BeforeEach void init() { - doReturn(TokenAttributeTestFactory.VALUE).when(service).getValue(any(), any()); + doReturn(TokenAttributeTestFactory.NAME).when(service).getTrustLevelKey(); } @Test void shouldCallGetMappedValue() { getTrustLevel(); - verify(service).getValue(TokenAttributeTestFactory.asMap(), TokenAttributes.TRUST_LEVEL_KEY); + verify(service).getTrustLevelKey(); } @Test @@ -714,52 +714,72 @@ class SamlAttributeServiceTest { } @Nested - class TestGetValue { + class TestIsNotNamedAttribute { - private static final String MAPPED_KEY = UUID.randomUUID().toString(); + private static final String KEY = UUID.randomUUID().toString(); @Test - void shouldReturnValueByMappedKey() { - when(tokenValidationProperty.getMappings()).thenReturn(Map.of(TokenAttributeTestFactory.NAME, MAPPED_KEY)); + void shouldReturnTrueWhenNotMapped() { + var random = new Random(); + doReturn("postfachIdKey-" + random.nextInt()).when(service).getPostfachIdKey(); + doReturn("trustLevelKey-" + random.nextInt()).when(service).getTrustLevelKey(); - var result = service.getValue(Map.of(MAPPED_KEY, TokenAttributeTestFactory.VALUE), TokenAttributeTestFactory.NAME); + var result = isNotMappedField(); - assertThat(result).isEqualTo(TokenAttributeTestFactory.VALUE); + assertThat(result).isTrue(); } @Test - void shouldReturnValueByKey() { - var result = service.getValue(TokenAttributeTestFactory.asMap(), TokenAttributeTestFactory.NAME); + void shouldReturnFalseWhenMapped() { + doReturn(KEY).when(service).getPostfachIdKey(); - assertThat(result).isEqualTo(TokenAttributeTestFactory.VALUE); + var result = isNotMappedField(); + + assertThat(result).isFalse(); + } + + private boolean isNotMappedField() { + return service.isNotNamedAttribute(Map.entry(KEY, TokenAttributeTestFactory.VALUE)); } } @Nested - class TestIsNotNamedField { + class TestGetPostfachIdKey { - private static final String KEY = UUID.randomUUID().toString(); + @Test + void shouldReturnDefaultKey() { + var result = service.getPostfachIdKey(); + + assertThat(result).isEqualTo(TokenAttributes.POSTFACH_ID_KEY); + } @Test - void shouldReturnTrueWhenNotMapped() { - var result = isNotMappedField(); + void shouldReturnMappedKey() { + when(tokenValidationProperty.getMappings()).thenReturn(Map.of(TokenAttributes.POSTFACH_ID_KEY, TokenAttributeTestFactory.NAME)); - assertThat(result).isTrue(); + var result = service.getPostfachIdKey(); + + assertThat(result).isEqualTo(TokenAttributeTestFactory.NAME); } + } - @DisplayName("should return false when") - @ParameterizedTest(name = "key is {0}") - @ValueSource(strings = { TokenAttributes.POSTFACH_ID_KEY, TokenAttributes.TRUST_LEVEL_KEY }) - void shouldReturnFalseWhenMapped(String mappedKey) { - when(tokenValidationProperty.getMappings()).thenReturn(Map.of(KEY, mappedKey)); + @Nested + class TestGetTrustLevelKey { - var result = isNotMappedField(); + @Test + void shouldReturnDefaultKey() { + var result = service.getTrustLevelKey(); - assertThat(result).isFalse(); + assertThat(result).isEqualTo(TokenAttributes.TRUST_LEVEL_KEY); } - private boolean isNotMappedField() { - return service.isNotNamedField(Map.entry(KEY, TokenAttributeTestFactory.VALUE)); + @Test + void shouldReturnMappedKey() { + when(tokenValidationProperty.getMappings()).thenReturn(Map.of(TokenAttributes.TRUST_LEVEL_KEY, TokenAttributeTestFactory.NAME)); + + var result = service.getTrustLevelKey(); + + assertThat(result).isEqualTo(TokenAttributeTestFactory.NAME); } } 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 index 78d6ed09c04855e530418483ac6b45a271faffad..470c1f5ab5857f3484932f1d12c7fde24b9f2213 100644 --- 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 @@ -71,14 +71,14 @@ class SamlConfigurationTest { void init() { when(tokenValidationProperty.getIdpEntityId()).thenReturn(IDP_ENTITY_ID); when(tokenValidationProperties.getEntities()).thenReturn(List.of(tokenValidationProperty)); - doReturn(tokenValidationService).when(configuration).samlTokenService(any()); + doReturn(tokenValidationService).when(configuration).samlAttributeService(any()); } @Test void shouldCallSamlTokenService() { configuration.samlServiceRegistry(tokenValidationProperties); - verify(configuration).samlTokenService(tokenValidationProperty); + verify(configuration).samlAttributeService(tokenValidationProperty); } @Test @@ -176,7 +176,7 @@ class SamlConfigurationTest { } private SamlAttributeService samlTokenService() { - return configuration.samlTokenService(tokenValidationProperty); + return configuration.samlAttributeService(tokenValidationProperty); } } } \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTestConfiguration.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTestConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..5b724a1f5b1371981f32bf0b00920dd51e95d7af --- /dev/null +++ b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTestConfiguration.java @@ -0,0 +1,82 @@ +/* + * 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.mockito.Mockito.*; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +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.EncryptedAssertion; +import org.opensaml.saml.saml2.encryption.Decrypter; +import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; +import org.springframework.context.annotation.Bean; + +import lombok.SneakyThrows; + +public class SamlTestConfiguration { + + public static final String POSTFACH_ID = UUID.randomUUID().toString(); + public static final String TRUST_LEVEL = "STORK-QAA-Level-1"; + + public static final Map<String, String> SAML_ATTRIBUTES_MAP = Map.of( + "urn:oid:1.2.40.0.10.2.1.1.261.94", TRUST_LEVEL, + "urn:oid:2.5.4.18", POSTFACH_ID, + "urn:oid:0.9.2342.19200300.100.1.3", "mail@mail.local", + "urn:oid:1.3.6.1.4.1.25484.494450.2", "Benutzername", + "urn:oid:2.5.4.42", "Ozg", + "urn:oid:2.5.4.4", "Mgm" + ); + + @SneakyThrows + @Bean + SamlTrustEngineFactory samlTrustEngineFactory() { + SignatureTrustEngine signatureTrustEngine = when(mock(SignatureTrustEngine.class).validate(any(), any())).thenReturn(true).getMock(); + return when(mock(SamlTrustEngineFactory.class).buildSamlTrustEngine(any())).thenReturn(signatureTrustEngine).getMock(); + } + + @SneakyThrows + @Bean + SamlDecrypterFactory samlDecrypterFactory() { + var attributes = SAML_ATTRIBUTES_MAP.entrySet().stream().map(e -> initAttributeMock(e.getKey(), e.getValue())).toList(); + AttributeStatement attributeStatement = when(mock(AttributeStatement.class).getAttributes()).thenReturn(attributes).getMock(); + Assertion assertion = when(mock(Assertion.class).getStatements()).thenReturn(List.of(attributeStatement)).getMock(); + Decrypter decrypter = when(mock(Decrypter.class).decrypt(any(EncryptedAssertion.class))).thenReturn(assertion).getMock(); + return when(mock(SamlDecrypterFactory.class).buildDecrypter(any())).thenReturn(decrypter).getMock(); + } + + Attribute initAttributeMock(String name, String value) { + Attribute attribute = mock(Attribute.class); + when(attribute.getName()).thenReturn(name); + XSString attrValue = when(mock(XSString.class).getValue()).thenReturn(value).getMock(); + when(attribute.getAttributeValues()).thenReturn(List.of(attrValue)); + return attribute; + } + +} \ No newline at end of file diff --git a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTrustEngineFactoryTestConfiguration.java b/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTrustEngineFactoryTestConfiguration.java deleted file mode 100644 index c018ca4195a0a113afd6e5cfee43c2f02e07a94d..0000000000000000000000000000000000000000 --- a/token-checker-server/src/test/java/de/ozgcloud/token/saml/SamlTrustEngineFactoryTestConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.mockito.Mockito.*; - -import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; -import org.springframework.context.annotation.Bean; - -import lombok.SneakyThrows; - -public class SamlTrustEngineFactoryTestConfiguration { - - @SneakyThrows - @Bean - SamlTrustEngineFactory samlTrustEngineFactory() { - SignatureTrustEngine signatureTrustEngine = mock(SignatureTrustEngine.class); - when(signatureTrustEngine.validate(any(), any())).thenReturn(true); - return when(mock(SamlTrustEngineFactory.class).buildSamlTrustEngine(any())).thenReturn(signatureTrustEngine).getMock(); - } - -}