Skip to content
Snippets Groups Projects
Commit fb1183b1 authored by OZGCloud's avatar OZGCloud
Browse files

Merge pull request 'OZG-4367 Umlaute-in-Feldnamen-aus-FC-uebertragen' (#100)...

Merge pull request 'OZG-4367 Umlaute-in-Feldnamen-aus-FC-uebertragen' (#100) from OZG-4367-Umlaute-in-Feldnamen-aus-FC-uebertragen into master

Reviewed-on: https://git.ozg-sh.de/ozgcloud-app/eingang-manager/pulls/100
parents 54676067 ae286bbf
No related branches found
No related tags found
No related merge requests found
...@@ -38,19 +38,19 @@ ...@@ -38,19 +38,19 @@
<properties> <properties>
<formcycle-interface.version>${project.version}</formcycle-interface.version> <formcycle-interface.version>${project.version}</formcycle-interface.version>
<jsoup.version>1.17.2</jsoup.version>
</properties> </properties>
<dependencies> <dependencies>
<!--own project--> <!--own project-->
<dependency> <dependency>
<groupId>de.ozgcloud.vorgang</groupId> <groupId>de.ozgcloud.eingang</groupId>
<artifactId>vorgang-manager-utils</artifactId> <artifactId>formcycle-adapter-interface</artifactId>
<version>${formcycle-interface.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>de.ozgcloud.vorgang</groupId> <groupId>de.ozgcloud.eingang</groupId>
<artifactId>vorgang-manager-utils</artifactId> <artifactId>semantik-adapter</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>de.ozgcloud.eingang</groupId> <groupId>de.ozgcloud.eingang</groupId>
...@@ -58,6 +58,16 @@ ...@@ -58,6 +58,16 @@
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-utils</artifactId>
</dependency>
<dependency>
<groupId>de.ozgcloud.vorgang</groupId>
<artifactId>vorgang-manager-utils</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<!--spring--> <!--spring-->
<dependency> <dependency>
...@@ -69,15 +79,10 @@ ...@@ -69,15 +79,10 @@
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>de.ozgcloud.eingang</groupId> <groupId>org.jsoup</groupId>
<artifactId>formcycle-adapter-interface</artifactId> <artifactId>jsoup</artifactId>
<version>${formcycle-interface.version}</version> <version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>de.ozgcloud.eingang</groupId>
<artifactId>semantik-adapter</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
......
...@@ -65,6 +65,7 @@ class FormDataController { ...@@ -65,6 +65,7 @@ class FormDataController {
private final FormCycleFormDataMapper mapper; private final FormCycleFormDataMapper mapper;
private final SemantikAdapter semantikAdapter; private final SemantikAdapter semantikAdapter;
private final VorgangNummerSupplier vorgangNummerSupplier; private final VorgangNummerSupplier vorgangNummerSupplier;
private final FormDataHtmlCleaner formDataHtmlCleaner;
@PostMapping(consumes = "multipart/form-data", produces = HTTP_TYPE_PROTOBUF) @PostMapping(consumes = "multipart/form-data", produces = HTTP_TYPE_PROTOBUF)
public FormCycleConfirmationResponse receiveFormData(@RequestPart FormCycleFormData formData, public FormCycleConfirmationResponse receiveFormData(@RequestPart FormCycleFormData formData,
...@@ -72,6 +73,7 @@ class FormDataController { ...@@ -72,6 +73,7 @@ class FormDataController {
@RequestPart(required = false) Optional<Collection<MultipartFile>> attachments) { @RequestPart(required = false) Optional<Collection<MultipartFile>> attachments) {
FormData mappedFormData = mapper.toFormData(formData); FormData mappedFormData = mapper.toFormData(formData);
mappedFormData = formDataHtmlCleaner.clean(mappedFormData);
mappedFormData = addRepresentations(representations, mappedFormData); mappedFormData = addRepresentations(representations, mappedFormData);
mappedFormData = addFiles(formData, attachments, mappedFormData); mappedFormData = addFiles(formData, attachments, mappedFormData);
mappedFormData = addServiceKonto(formData, mappedFormData); mappedFormData = addServiceKonto(formData, mappedFormData);
......
/*
* Copyright (C) 2023 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.eingang.formcycle;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Safelist;
import org.springframework.stereotype.Component;
import de.ozgcloud.eingang.common.formdata.FormData;
@Component
public class FormDataHtmlCleaner {
public FormData clean(FormData formData) {
return formData.toBuilder().formData(cleanFormData(formData.getFormData())).build();
}
Map<String, Object> cleanFormData(Map<String, Object> formData) {
return formData.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> cleanValue(entry.getValue())));
}
@SuppressWarnings("unchecked")
Object cleanValue(Object value) {
if (value instanceof Map) {
return cleanFormData((Map<String, Object>) value);
} else if (value instanceof Collection<?> values) {
return values.stream().map(this::cleanValue).toList();
} else if (value instanceof String valueString) {
return parseHtml(valueString);
}
return value;
}
Object parseHtml(String html) {
var jsoupDocument = Jsoup.parse(html);
var outputSettings = new Document.OutputSettings(); // keep new lines
outputSettings.prettyPrint(false);
jsoupDocument.outputSettings(outputSettings);
var innerHtml = jsoupDocument.html().replace("\\\\n", "\n");
return Jsoup.clean(innerHtml, "", Safelist.none(), outputSettings);
}
}
...@@ -71,6 +71,8 @@ class FormDataControllerTest { ...@@ -71,6 +71,8 @@ class FormDataControllerTest {
private SemantikAdapter semantikAdapter; private SemantikAdapter semantikAdapter;
@Mock @Mock
private VorgangNummerSupplier vorgangNummerSupplier; private VorgangNummerSupplier vorgangNummerSupplier;
@Mock
private FormDataHtmlCleaner htmlCleaner;
private MockMvc mockMvc; private MockMvc mockMvc;
...@@ -93,6 +95,7 @@ class FormDataControllerTest { ...@@ -93,6 +95,7 @@ class FormDataControllerTest {
@BeforeEach @BeforeEach
void init() { void init() {
when(htmlCleaner.clean(any())).thenReturn(mappedFormData);
when(mapper.toFormData(any())).thenReturn(mappedFormData); when(mapper.toFormData(any())).thenReturn(mappedFormData);
when(vorgangNummerSupplier.get()).thenReturn(VORGANG_NUMMER); when(vorgangNummerSupplier.get()).thenReturn(VORGANG_NUMMER);
} }
...@@ -111,7 +114,7 @@ class FormDataControllerTest { ...@@ -111,7 +114,7 @@ class FormDataControllerTest {
@Test @Test
@SneakyThrows @SneakyThrows
void shouldRespondeWithVorgangNummer() { void shouldResponseWithVorgangNummer() {
var confirmation = FormCycleConfirmationResponse.parseFrom( var confirmation = FormCycleConfirmationResponse.parseFrom(
doPostRequest().andReturn().getResponse().getContentAsByteArray()); doPostRequest().andReturn().getResponse().getContentAsByteArray());
...@@ -127,6 +130,13 @@ class FormDataControllerTest { ...@@ -127,6 +130,13 @@ class FormDataControllerTest {
assertThat(formDataCaptor.getValue().getHeader().getRequestId()).isEqualTo(VORGANG_NUMMER); assertThat(formDataCaptor.getValue().getHeader().getRequestId()).isEqualTo(VORGANG_NUMMER);
} }
@Test
void shouldCallHtmlCleaner() {
doPostRequest();
verify(htmlCleaner).clean(any());
}
@Test @Test
void shouldCallMapper() { void shouldCallMapper() {
doPostRequest(); doPostRequest();
...@@ -157,8 +167,9 @@ class FormDataControllerTest { ...@@ -157,8 +167,9 @@ class FormDataControllerTest {
@BeforeEach @BeforeEach
void init() { void init() {
when(mapper.toFormData(any())) var formData = FormDataTestFactory.createBuilder().clearRepresentations().numberOfRepresentations(0).build();
.thenReturn(FormDataTestFactory.createBuilder().clearRepresentations().numberOfRepresentations(0).build()); when(mapper.toFormData(any())).thenReturn(formData);
when(htmlCleaner.clean(any())).thenReturn(formData);
} }
@Test @Test
...@@ -195,8 +206,9 @@ class FormDataControllerTest { ...@@ -195,8 +206,9 @@ class FormDataControllerTest {
class Attachments { class Attachments {
@BeforeEach @BeforeEach
void init() { void init() {
when(mapper.toFormData(any())) var formData = FormDataTestFactory.createBuilder().clearAttachments().numberOfAttachments(0).build();
.thenReturn(FormDataTestFactory.createBuilder().clearAttachments().numberOfAttachments(0).build()); when(mapper.toFormData(any())).thenReturn(formData);
when(htmlCleaner.clean(any())).thenReturn(formData);
} }
@Test @Test
......
/*
* Copyright (C) 2023 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.eingang.formcycle;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.Map;
import org.assertj.core.data.MapEntry;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import de.ozgcloud.eingang.common.formdata.FormData;
class FormDataHtmlCleanerTest {
private static final String KEY = "key";
private static final Object VALUE = "value";
@Spy
@InjectMocks
private FormDataHtmlCleaner cleaner;
@Nested
class TestClean {
private final static Map<String, Object> FORM_DATA_MAP = Map.of(KEY, VALUE);
private final static FormData FORM_DATA = FormData.builder().formData(FORM_DATA_MAP).build();
@Test
void shouldCallCleanFormData() {
cleaner.clean(FORM_DATA);
verify(cleaner).cleanFormData(FORM_DATA_MAP);
}
@Test
void shouldSetCleanedFormData() {
var cleanedFormData = Map.of(KEY, VALUE);
doReturn(cleanedFormData).when(cleaner).cleanFormData(anyMap());
var result = cleaner.clean(FORM_DATA);
assertThat(result.getFormData()).isSameAs(cleanedFormData);
}
}
@Nested
class TestCleanFormData {
@Test
void shouldCallCleanValue() {
cleaner.cleanFormData(Map.of(KEY, VALUE));
verify(cleaner).cleanValue(VALUE);
}
@Test
void shouldReturnCleanedMap() {
var cleanedValue = "noHtml";
doReturn(cleanedValue).when(cleaner).cleanValue(any());
var result = cleaner.cleanFormData(Map.of(KEY, VALUE));
assertThat(result).containsOnly(MapEntry.entry(KEY, cleanedValue));
}
}
@Nested
class TestCleanValue {
@Nested
class TestCleanMap {
@Test
void shouldCallCleanFormData() {
var expectedMap = Map.of(KEY, VALUE);
cleaner.cleanValue(expectedMap);
verify(cleaner).cleanFormData(expectedMap);
}
@Test
void shouldReturnValue() {
var expectedMap = Map.of(KEY, VALUE);
doReturn(expectedMap).when(cleaner).cleanFormData(anyMap());
var result = cleaner.cleanValue(Map.of("a", "b"));
assertThat(result).isSameAs(expectedMap);
}
}
@Nested
class TestCleanCollection {
@Test
void shouldCallCleanValue() {
cleaner.cleanValue(List.of(VALUE));
verify(cleaner).cleanValue(VALUE);
}
@Test
void shouldReturnValue() {
doReturn(List.of(VALUE)).when(cleaner).cleanValue(any());
var result = cleaner.cleanValue(List.of("a"));
assertThat(result).isInstanceOf(List.class).asList().containsExactly(VALUE);
}
}
@Nested
class TestCleanString {
@Test
void shouldCallParseHtml() {
var stringValue = VALUE.toString();
cleaner.cleanValue(VALUE);
verify(cleaner).parseHtml(stringValue);
}
@Test
void shouldReturnValue() {
var cleanedValue = "noHtml";
doReturn(cleanedValue).when(cleaner).parseHtml(anyString());
var result = cleaner.cleanValue(VALUE);
assertThat(result).isEqualTo(cleanedValue);
}
}
@Test
void shouldReturnUnmodifiedValue() {
var value = 1;
var result = cleaner.cleanValue(value);
assertThat(result).isEqualTo(1);
verify(cleaner, never()).parseHtml(any());
}
}
@Nested
class TestHtmlCleaner {
static final String KEY_LABEL = "label";
static final String KEY_VALUE = "value";
static final Map<String, Object> FORM_DATA_MAP = Map.of("tf1", Map.of(
KEY_LABEL, "<p><em>&Auml;</em></p>",
KEY_VALUE, "Ä - Wert"),
"tf2", Map.of(
KEY_LABEL, "<p><strong>&Ouml;</strong></p>",
KEY_VALUE, "Ö - Wert"),
"fs1", Map.of(
KEY_LABEL, "Ü",
KEY_VALUE, Map.of(
"tf3", Map.of(
KEY_LABEL, " <p><s>Label mit</s> &szlig;</p>",
KEY_VALUE, "ein Text mit ß und <html><body><h1>Hello</h1><body><html>")),
"tf4", Map.of(
KEY_LABEL, "<p><span style=\"background-color:#1abc9c;\">&auml;</span></p>",
KEY_VALUE, "Text"),
"ed1", Map.of(
KEY_LABEL,
"<ol>\n\t<li><em><strong><u>&ouml;</u></strong></em></li>\n\t<li><span style=\"color:#e74c3c;\">&uuml;</span></li>\n</ol>",
KEY_VALUE, "TExt\nmit\n Leerzeichen\nund\n Umbrüchen"
)));
static final Map<String, Object> EXPECTED_MAP = Map.of("tf1", Map.of(
KEY_LABEL, "Ä",
KEY_VALUE, "Ä - Wert"),
"tf2", Map.of(
KEY_LABEL, "Ö",
KEY_VALUE, "Ö - Wert"),
"fs1", Map.of(
KEY_LABEL, "Ü",
KEY_VALUE, Map.of(
"tf3", Map.of(
KEY_LABEL, "Label mit ß",
KEY_VALUE, "ein Text mit ß und Hello")),
"tf4", Map.of(
KEY_LABEL, "ä",
KEY_VALUE, "Text"),
"ed1", Map.of(
KEY_LABEL,
"\n\tö\n\tü\n",
KEY_VALUE, "TExt\nmit\n Leerzeichen\nund\n Umbrüchen"
)));
@Test
void shouldCleanHtml() {
var result = cleaner.clean(FormData.builder().formData(FORM_DATA_MAP).build());
assertThat(result.getFormData()).isEqualTo(EXPECTED_MAP);
}
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment