diff --git a/.gitignore b/.gitignore
index 6baf7914c05c3e7a22256d135f4685ebb903b230..81229959e71dccae6f8e5244cfd43d988d529aa9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,8 @@ target/
 !**/src/test/**/target/
 .gradle/
 bin/
+*.crt
+*.key
 
 ### STS ###
 .apt_generated
@@ -16,7 +18,6 @@ bin/
 .sts4-cache
 
 ### IntelliJ IDEA ###
-.idea
 *.iws
 *.iml
 *.ipr
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b4208d1f50f3d62744b4eb594d81fb029e332ed4
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,9 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+/sonarlint/
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c7b7b708dab82c08c005e3bdd77c6b202f9676d5
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,75 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <JavaCodeStyleSettings>
+      <option name="GENERATE_FINAL_PARAMETERS" value="true" />
+      <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
+      <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1" />
+      <option name="IMPORT_LAYOUT_TABLE">
+        <value>
+          <package name="" withSubpackages="true" static="true" />
+          <emptyLine />
+          <package name="java" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="jakarta" withSubpackages="true" static="false" />
+          <package name="javax" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="org" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="com" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="" withSubpackages="true" static="false" />
+        </value>
+      </option>
+      <option name="ALIGN_MULTILINE_RECORDS" value="false" />
+      <option name="ALIGN_TYPES_IN_MULTI_CATCH" value="false" />
+      <option name="JD_P_AT_EMPTY_LINES" value="false" />
+      <option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
+    </JavaCodeStyleSettings>
+    <JetCodeStyleSettings>
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </JetCodeStyleSettings>
+    <codeStyleSettings language="JAVA">
+      <option name="RIGHT_MARGIN" value="150" />
+      <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
+      <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
+      <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
+      <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
+      <option name="KEEP_BLANK_LINES_BETWEEN_PACKAGE_DECLARATION_AND_HEADER" value="1" />
+      <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
+      <option name="INDENT_CASE_FROM_SWITCH" value="false" />
+      <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
+      <option name="ALIGN_MULTILINE_RESOURCES" value="false" />
+      <option name="ALIGN_MULTILINE_FOR" value="false" />
+      <option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
+      <option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
+      <option name="CALL_PARAMETERS_WRAP" value="1" />
+      <option name="METHOD_PARAMETERS_WRAP" value="1" />
+      <option name="RESOURCE_LIST_WRAP" value="5" />
+      <option name="EXTENDS_LIST_WRAP" value="1" />
+      <option name="THROWS_LIST_WRAP" value="1" />
+      <option name="EXTENDS_KEYWORD_WRAP" value="1" />
+      <option name="THROWS_KEYWORD_WRAP" value="1" />
+      <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
+      <option name="BINARY_OPERATION_WRAP" value="1" />
+      <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
+      <option name="TERNARY_OPERATION_WRAP" value="5" />
+      <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
+      <option name="ARRAY_INITIALIZER_WRAP" value="1" />
+      <option name="WRAP_COMMENTS" value="true" />
+      <option name="ASSERT_STATEMENT_COLON_ON_NEXT_LINE" value="true" />
+      <option name="ENUM_CONSTANTS_WRAP" value="1" />
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="2" />
+        <option name="USE_TAB_CHARACTER" value="true" />
+      </indentOptions>
+    </codeStyleSettings>
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="USE_TAB_CHARACTER" value="true" />
+      </indentOptions>
+    </codeStyleSettings>
+    <codeStyleSettings language="kotlin">
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </codeStyleSettings>
+  </code_scheme>
+</component>
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000000000000000000000000000000000000..79ee123c2b23e069e35ed634d687e17f731cc702
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0d20ae9d47b22a2d22e9c86c627d8d5fbaf4b83e
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <annotationProcessing>
+      <profile default="true" name="Default" enabled="true" />
+      <profile name="Gradle Imported" enabled="true">
+        <outputRelativeToContentRoot value="true" />
+        <processorPath useClasspath="false">
+          <entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.30/f195ee86e6c896ea47a1d39defbe20eb59cd149d/lombok-1.18.30.jar" />
+        </processorPath>
+      </profile>
+      <profile name="Maven default annotation processors profile" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+      </profile>
+      <profile name="Annotation profile for OZG-Cloud Antragsraum" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <processorPath useClasspath="false">
+          <entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.30/lombok-1.18.30.jar" />
+          <entry name="$MAVEN_REPOSITORY$/org/mapstruct/mapstruct-processor/1.5.5.Final/mapstruct-processor-1.5.5.Final.jar" />
+          <entry name="$MAVEN_REPOSITORY$/org/mapstruct/mapstruct/1.5.5.Final/mapstruct-1.5.5.Final.jar" />
+        </processorPath>
+        <module name="antragsraum-server" />
+      </profile>
+    </annotationProcessing>
+    <bytecodeTargetLevel target="21" />
+  </component>
+  <component name="JavacSettings">
+    <option name="ADDITIONAL_OPTIONS_OVERRIDE">
+      <module name="antragsraum-server" options="-parameters -Amapstruct.defaultComponentModel=spring -Amapstruct.unmappedTargetPolicy=WARN" />
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/copyright/Eupl1_2.xml b/.idea/copyright/Eupl1_2.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8b51bb2301fbdd90e6ee5801f4f7713a2a0dac5a
--- /dev/null
+++ b/.idea/copyright/Eupl1_2.xml
@@ -0,0 +1,6 @@
+<component name="CopyrightManager">
+  <copyright>
+    <option name="notice" value="Copyright (c) &amp;#36;originalComment.match(&quot;Copyright \(c\) (\d+)&quot;, 1, &quot;-&quot;, &quot;&amp;#36;today.year&quot;)&amp;#36;today.year.   Das Land Schleswig-Holstein vertreten durch den Ministerpräsidenten&#10;des Landes Schleswig-Holstein Staatskanzlei Abteilung Digitalisierung und zentrales IT-Management der Landesregierung.&#10;&#10;Lizenziert unter der EUPL, Version 1.2 oder - sobald&#10;diese von der Europäischen Kommission genehmigt wurden -&#10;Folgeversionen der EUPL (&quot;Lizenz&quot;);&#10;Sie dürfen dieses Werk ausschließlich gemäß&#10;dieser Lizenz nutzen.&#10;Eine Kopie der Lizenz finden Sie hier:&#10;&#10;https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12&#10;&#10;Sofern nicht durch anwendbare Rechtsvorschriften&#10;gefordert oder in schriftlicher Form vereinbart, wird&#10;die unter der Lizenz verbreitete Software &quot;so wie sie&#10;ist&quot;, OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -&#10;ausdrücklich oder stillschweigend - verbreitet.&#10;Die sprachspezifischen Genehmigungen und Beschränkungen&#10;unter der Lizenz sind dem Lizenztext zu entnehmen. " />
+    <option name="myName" value="Eupl1.2" />
+  </copyright>
+</component>
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4c8996918f5c5ce3236fb74f10a56f1ba8bf84fa
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="CopyrightManager">
+  <settings>
+    <module2copyright>
+      <element module="Project Files" copyright="Eupl1.2" />
+    </module2copyright>
+  </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..60d155daf929f118b22c909238ecdc67d4f700cf
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
+    <file url="file://$PROJECT_DIR$/server/src/main/java" charset="UTF-8" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3e3960b71eaef30230fcaeef29ebc18216501448
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleMigrationSettings" migrationVersion="1" />
+</project>
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2f42e537a699dc92219bd00691e0e37d4200b562
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RemoteRepositoriesConfiguration">
+    <remote-repository>
+      <option name="id" value="shibboleth" />
+      <option name="name" value="Shibboleth Maven Repo" />
+      <option name="url" value="https://build.shibboleth.net/nexus/content/repositories/releases" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Central Repository" />
+      <option name="url" value="https://repo.maven.apache.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="shibboleth-releases" />
+      <option name="name" value="Shibboleth Releases Repository" />
+      <option name="url" value="https://build.shibboleth.net/maven/releases/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Maven Central" />
+      <option name="url" value="https://repo1.maven.org/maven2/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Maven Central repository" />
+      <option name="url" value="https://repo1.maven.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="nexus" />
+      <option name="name" value="Ozg nexus" />
+      <option name="url" value="https://nexus.ozg-sh.de/repository/ozg-ozg-releases" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="maven" />
+      <option name="name" value="maven" />
+      <option name="url" value="https://artifacts.mgm-tp.com/artifactory/maven-repos" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="maven2" />
+      <option name="name" value="maven2" />
+      <option name="url" value="https://build.shibboleth.net/nexus/content/repositories/releases/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="MavenRepo" />
+      <option name="name" value="MavenRepo" />
+      <option name="url" value="https://repo.maven.apache.org/maven2/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="project-repository" />
+      <option name="name" value="project-repository" />
+      <option name="url" value="file://$PROJECT_DIR$/server/tmpRepo" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="jboss.community" />
+      <option name="name" value="JBoss Community repository" />
+      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="ozg-snapshots-nexus" />
+      <option name="name" value="ozg-snapshots" />
+      <option name="url" value="https://nexus.ozg-sh.de/repository/ozg-snapshots/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="local-repo" />
+      <option name="name" value="local-repo" />
+      <option name="url" value="file://$PROJECT_DIR$/server/lib" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="ozg-nexus" />
+      <option name="name" value="ozg-releases" />
+      <option name="url" value="https://nexus.ozg-sh.de/repository/ozg-releases/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="shibboleth-thirdparty" />
+      <option name="name" value="Shibboleth Thirdparty Repository" />
+      <option name="url" value="https://build.shibboleth.net/maven/thirdparty/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="nexus" />
+      <option name="name" value="Ozg nexus" />
+      <option name="url" value="https://nexus.ozg-sh.de/repository/ozg-releases" />
+    </remote-repository>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..290d6c7b10d9725454ff8e20c97a70786d11f5eb
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="FrameworkDetectionExcludesConfiguration">
+    <file type="web" url="file://$PROJECT_DIR$" />
+  </component>
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b507f1bc5017a57b35b8890a6981c5cec9e6709c
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/server/antragsraum-server.iml" filepath="$PROJECT_DIR$/server/antragsraum-server.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum.integrationTest.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum.integrationTest.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum.main.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum.main.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.integrationTest.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.integrationTest.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.main.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.main.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.test.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum-server/ozg-antragsraum.ozg-antragsraum-server.test.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/keycloak/ozg-antragsraum.ozg-keycloak.iml" filepath="$PROJECT_DIR$/.idea/modules/keycloak/ozg-antragsraum.ozg-keycloak.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/ozg-antragsraum.test.iml" filepath="$PROJECT_DIR$/.idea/modules/ozg-antragsraum.test.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/sonarlint.xml b/.idea/sonarlint.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0e83d2e68b218e8d268a339d9812aec0b616780e
--- /dev/null
+++ b/.idea/sonarlint.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="SonarLintProjectSettings">
+    <option name="bindingEnabled" value="true" />
+    <option name="projectKey" value="de.ozgcloud:antragsraum" />
+    <option name="serverId" value="ozg-cloud" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2b63946d5b31084bbb7dda418ceb3d75eb686373
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..bf82ff01c6cdae4a1bb754a6e062954d77ac5c11
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..30052152b244e97532823b4f5bfc86c7e8408645
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
diff --git a/JenkinsBuildFile b/JenkinsBuildFile
deleted file mode 100644
index 6ccdf8edcad588a75bf6ca3c119ddcbfb1f5c8b1..0000000000000000000000000000000000000000
--- a/JenkinsBuildFile
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/usr/bin/env groovy
-
-pipeline {
-    agent { label 'linux-node' }
-    options {
-        ansiColor('xterm')
-        disableConcurrentBuilds()
-        buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5'))
-    }
-    tools {
-        gradle 'Gradle8'
-        jdk 'OpenJDK17'
-    }
-    environment {
-        GRADLE_USER_HOME = "${WORKSPACE}/.gradle"
-    }
-    parameters {
-        booleanParam(
-            defaultValue: false,
-            name: 'PUBLISH',
-            description: 'Publish to Artifactory\nNote: Builds on "dev" and tags are always pushed to Artifactory.'
-        )
-    }
-
-    stages {
-        stage('Build') {
-            steps {
-                script {
-                    buildEnvironment {
-                        sh 'gradle :ozg-antragsraum-server:build'
-                    }
-                }
-            }
-        }
-
-        stage('Sonar & Quality Gate') {
-            steps {
-                withCredentials([
-                usernamePassword(credentialsId: 'ci-user', usernameVariable: 'repository_username', passwordVariable: 'repository_password'),
-                string(variable: 'sonar_token', credentialsId: 'ci-user-sonarqube-token')]) {
-                    configFileProvider([configFile(fileId: 'gradle-settings', targetLocation: '.gradle/init.d/settings.gradle')]) {
-                        script {
-                            sh "gradle sonar"
-                        }
-                    }
-                }
-            }
-        }
-
-        stage('Build + Publish Container') {
-            when {
-                anyOf {
-                    buildingTag()
-                    branch 'dev'
-                    expression { params.PUBLISH == true }
-                }
-            }
-            steps {
-                script {
-                    buildEnvironment {
-                        currentBuild.displayName = getBuildName()
-                        sh 'gradle :ozg-antragsraum-server:bootBuildImage --publishImage'
-                    }
-                }
-            }
-        }
-
-		stage('Publish Helm Chart'){
-            when {
-                anyOf {
-                    buildingTag()
-                    branch 'dev'
-                    expression { params.PUBLISH == true }
-                }
-            }
-            steps{
-                script{
-                    buildEnvironment{
-                        sh 'gradle :ozg-antragsraum-server:helmPublish'
-                    }
-                }
-            }
-
-		}	
-    }
-    post {
-        always {
-            junit allowEmptyResults: true, testResults: 'build/test-results/**/*.xml'
-        }
-        unsuccessful {
-            emailext body: "${currentBuild.currentResult}: Job ${env.JOB_NAME} build ${env.BUILD_NUMBER}\nMore info at: ${env.BUILD_URL}",
-                recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']],
-                subject: "[Jenkins] OZG - AntragsRaum build ${currentBuild.currentResult}: Job ${env.JOB_NAME}"
-        }
-    }
-}
-
-String getBuildName() {
-    String projectVersion = sh(script: 'gradle :getVersion -q', returnStdout: true)
-    return projectVersion.trim()
-}
-
-void buildEnvironment(Closure body) {
-    withDockerRegistry(url: 'https://docker-repos.dockerregistry.mgm-tp.com', credentialsId: 'ci-user') {
-        withCredentials([
-                usernamePassword(// for the maven repository
-                        credentialsId: 'ci-user',
-                        usernameVariable: 'repository_username',
-                        passwordVariable: 'repository_password'),
-                usernamePassword( // for the docker builder repository
-                        credentialsId: 'ci-user-nexus',
-                        usernameVariable: 'repository_pull_username',
-                        passwordVariable: 'repository_pull_password'),
-                usernamePassword(// for the docker publish repository
-                        credentialsId: 'ci-user-nexus',
-                        usernameVariable: 'repository_publish_username',
-                        passwordVariable: 'repository_publish_password'),
-                usernamePassword(// for the helm repository
-                        credentialsId: 'ci-user-nexus',
-                        usernameVariable: 'repository_helm_username',
-                        passwordVariable: 'repository_helm_password')
-
-        ]) {
-            configFileProvider([configFile(fileId: 'gradle-settings', targetLocation: "${GRADLE_USER_HOME}/init.d/settings.gradle")]) {
-                nodejs(configId: 'npmrc', nodeJSInstallationName: 'Node18') {
-                    body()
-                }
-            }
-        }
-    }
-}
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000000000000000000000000000000000000..060d133e47c8fb006123792c8ca17e21a31b9f28
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,347 @@
+pipeline {
+    agent {
+       node {
+           label 'ozgcloud-jenkins-build-agent-jdk21'
+        }
+    }
+
+    environment {
+        BLUE_OCEAN_URL = "https://jenkins.infra.ozg-cloud.systems/job/antragsraum-server/job/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/"
+        RELEASE_REGEX = /\d+.\d+.\d+/
+        SNAPSHOT_REGEX = /\d+.\d+.\d+-SNAPSHOT/
+        FAILED_STAGE = ""
+        SH_SUCCESS_STATUS_CODE = 0
+    }
+
+    options {
+        timeout(time: 1, unit: 'HOURS')
+        disableConcurrentBuilds()
+        buildDiscarder(logRotator(numToKeepStr: '5'))
+    }
+
+    stages {
+        stage('Check Version') {
+            steps {
+                script {
+                    FAILED_STAGE = env.STAGE_NAME
+                    def rootVersion = getPomVersion('pom.xml')
+                    def serverVersion = getPomVersion('server/pom.xml')
+
+                    if(env.BRANCH_NAME == 'release'){
+                        if ( !(rootVersion ==~ RELEASE_REGEX) || !(serverVersion ==~ RELEASE_REGEX) ) {
+                            error("Keine Release Version für Branch ${env.BRANCH_NAME}.")
+                        }
+                    } else {
+                        if ( !(rootVersion ==~ SNAPSHOT_REGEX) || !(serverVersion ==~ SNAPSHOT_REGEX) ) {
+                            error("Keine Snapshot Version für Branch ${env.BRANCH_NAME}.")
+                        }
+                    }
+
+                    if( !(rootVersion == serverVersion)){
+                        error("Versionen sind nicht identisch")                        
+                    }
+                }
+            }
+        }
+        
+        stage('Build Antragsraum-Server') {
+          steps {
+                script {
+                    FAILED_STAGE=env.STAGE_NAME
+
+                    configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
+                        sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS clean install -Dmaven.wagon.http.retryHandler.count=3'
+                        if (env.BRANCH_NAME == 'dev') {
+                            try {
+                                withSonarQubeEnv('sonarqube-ozg-sh'){
+                                    sh 'mvn -s $MAVEN_SETTINGS sonar:sonar'
+                                }
+                            } catch (Exception e) {
+                                unstable("SonarQube failed")
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        
+        stage('Deploy to Nexus'){
+             when {
+                anyOf {
+                    branch 'dev'
+                    branch 'release'
+                }
+            }
+           
+            steps {
+                script {
+                    FAILED_STAGE = env.STAGE_NAME
+                }
+                configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
+                    sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS -DskipTests deploy -Dmaven.wagon.http.retryHandler.count=3'
+                }
+            }
+        }
+
+	   stage('Build Docker image') {
+          steps {
+                script {
+                    FAILED_STAGE=env.STAGE_NAME
+                }
+               configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
+                    dir('server') {
+                        sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3'
+                    }
+                }
+
+           }
+        }
+
+        stage('Tag and Push Docker image') {
+            steps {
+                script {
+                    FAILED_STAGE=env.STAGE_NAME
+                    IMAGE_TAG = generateImageTag()
+
+                    tagAndPushDockerImage(IMAGE_TAG)
+
+                    if (env.BRANCH_NAME == 'dev') {
+                        tagAndPushDockerImage('snapshot-latest')
+                        
+                    }
+                    else if (env.BRANCH_NAME == 'release') {
+                        tagAndPushDockerImage('latest')
+                    }
+                }
+            }
+        }
+
+        stage('Test, build and deploy Helm Chart') {
+            steps {
+                script {
+                    FAILED_STAGE=env.STAGE_NAME
+                    HELM_CHART_VERSION = generateHelmChartVersion()
+
+                    dir('server') { sh "./run_helm_test.sh" } 
+
+                    dir('server/src/main/helm') {
+
+                        sh "helm package --version=${HELM_CHART_VERSION} ."
+
+                         deployHelmChart(HELM_CHART_VERSION)
+                    }
+                }
+            }
+        }        
+    
+
+        stage('Trigger Dev rollout') {
+            when {
+                branch 'dev'
+            }
+            steps {
+                script {
+                    FAILED_STAGE = env.STAGE_NAME
+
+                    cloneGitopsRepo()
+
+                    setNewDevVersion()
+
+                    pushDevGitopsRepo()
+                }
+            }
+        }
+        stage('Trigger Test rollout') {
+            when {
+                branch 'release'
+            }	
+            	
+            steps {	
+                script {	
+                    FAILED_STAGE = env.STAGE_NAME
+
+                    cloneGitopsRepo()
+
+                    setNewTestVersion()
+
+                    pushTestGitopsRepo()
+                }
+            }	
+        }
+        
+        stage ('OWASP Dependency-Check Vulnerabilities') {
+            steps {
+                    dependencyCheck additionalArguments: ''' 
+                        -o "./" 
+                        -s "./"
+                        -f "ALL" 
+                        -d /dependency-check-data
+                        --suppression dependency-check-supressions.xml
+                        --noupdate
+                        --disableKnownExploited
+                        --disableArchive
+                        --prettyPrint''', odcInstallation: 'dependency-check-owasp'
+
+                    dependencyCheckPublisher pattern: 'dependency-check-report.xml'
+            }
+        }
+
+    }
+    
+    post {
+        always{
+            junit testResults: '**/target/surefire-reports/*.xml', skipPublishingChecks: true
+        }
+        failure {
+            script {
+                if (env.BRANCH_NAME == 'dev' || env.BRANCH_NAME == 'release') {
+                    sendFailureMessage()
+                }
+            }
+        }
+    }
+}
+
+
+
+Void deployHelmChart(String helmChartVersion) {       
+    withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]){
+        if (env.BRANCH_NAME == 'release') {
+            result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps -F file=@antragsraum-server-'''+helmChartVersion+'''.tgz''', returnStdout: true
+        }
+        else {
+            result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps-snapshot -F file=@antragsraum-server-'''+helmChartVersion+'''.tgz''', returnStdout: true
+        }
+
+        if (result != '') {
+            error(result)
+        }
+    }
+}
+
+String generateHelmChartVersion() {
+    def chartVersion = getPomVersion('pom.xml')
+
+    if (env.BRANCH_NAME == 'dev') {
+        chartVersion += "-${env.GIT_COMMIT.take(7)}"
+    }
+    else if (env.BRANCH_NAME != 'release') {
+        chartVersion += "-${env.BRANCH_NAME}"
+    }
+
+    return chartVersion.replaceAll("_", "-")
+}
+
+Void tagAndPushDockerImage(String newTag){
+    withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
+        sh 'docker login docker.ozg-sh.de -u ${USER} -p ${PASSWORD}'
+
+        sh "docker tag docker.ozg-sh.de/antragsraum-server:build-latest docker.ozg-sh.de/antragsraum-server:${newTag}"
+        sh "docker push docker.ozg-sh.de/antragsraum-server:${newTag}"
+    }
+}
+
+String getPomVersion(String pomFile){
+    def pom = readMavenPom file: pomFile
+
+    return pom.version
+}
+
+String generateImageTag() {
+    def imageTag = "${env.BRANCH_NAME}-${getPomVersion('pom.xml')}"
+
+    if (env.BRANCH_NAME == 'dev') {
+        imageTag += "-${env.GIT_COMMIT.take(7)}"
+    }
+
+    return imageTag
+}
+
+Void configureGit() {
+    final email = "jenkins@ozg-sh.de"
+    final name = "jenkins"
+
+    dir("gitops") {
+        sh "git config user.email '${email}'"
+        sh "git config user.name '${name}'"
+    }
+}
+
+Void cloneGitopsRepo() {
+    withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) {
+        sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git'
+    }
+
+    configureGit()
+}
+
+Void setNewDevVersion() {
+    setNewGitopsVersion("dev")
+}
+
+Void setNewTestVersion() {
+    setNewGitopsVersion("test")
+}
+
+Void setNewGitopsVersion(String environment) {
+    dir("gitops") {
+        def envFile = "${environment}/application/values/antragsraum-server-values.yaml"
+
+        def envVersions = readYaml file: envFile
+
+        envVersions.antragsraum_server.image.tag = IMAGE_TAG
+        envVersions.antragsraum_server.helm.version = HELM_CHART_VERSION
+
+        writeYaml file: envFile, data: envVersions, overwrite: true
+    }
+}
+
+Void pushDevGitopsRepo() {
+    pushNewGitopsVersion('dev')
+}
+
+Void pushTestGitopsRepo() {
+    pushNewGitopsVersion('test')
+}
+
+Void pushNewGitopsVersion(String environment) {
+    dir('gitops') {
+        if (!hasValuesFileChanged(environment)) {
+            return
+        }
+
+        withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) {
+            sh "git add ${environment}/application/values/antragsraum-server-values.yaml"
+
+            sh "git commit -m 'jenkins rollout ${environment} antragsraum-server version ${IMAGE_TAG}'"
+            sh 'git push https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git'
+        }
+    }
+}
+
+Boolean hasValuesFileChanged(String environment) {
+    return sh (script: "git status | grep '${environment}/application/values/antragsraum-server-values.yaml'", returnStatus: true) == env.SH_SUCCESS_STATUS_CODE as Integer
+}
+
+Void sendFailureMessage() {
+    def room = ''
+    def data = """{"msgtype":"m.text", \
+                    "body":"AntragsraumServer: Build Failed. Stage: ${FAILED_STAGE} Build-ID: ${env.BUILD_NUMBER} Link: ${BLUE_OCEAN_URL}", \
+                    "format": "org.matrix.custom.html", \
+                    "formatted_body":"AntragsraumServer: Build Failed. Stage: ${FAILED_STAGE} Build-ID: <a href='${BLUE_OCEAN_URL}'>${env.BUILD_NUMBER}</a>"}"""
+       
+    if (env.BRANCH_NAME == 'dev') {
+        room = "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de"
+    }
+    else if (env.BRANCH_NAME == 'release') {
+        room = "!oWZpUGTFsxkJIYNfYg:matrix.ozg-sh.de"
+    }
+
+    sh "curl -XPOST -H 'authorization: Bearer ${getElementAccessToken()}' -d '${data}' https://matrix.ozg-sh.de/_matrix/client/v3/rooms/$room/send/m.room.message"
+}
+
+String getElementAccessToken() {
+    withCredentials([string(credentialsId: 'element-login-json', variable: 'LOGIN_JSON')]) {
+        return readJSON ( text: sh (script: '''curl -XPOST -d \"$LOGIN_JSON\" https://matrix.ozg-sh.de/_matrix/client/v3/login''', returnStdout: true)).access_token
+    }
+}
diff --git a/README.md b/README.md
index 19e9c9c0e8ce7460baf51f087fa18ab5c28bc5f1..303dae2be6915f8fd208c87a394ec9fe57b523c9 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ For further reference, please consider the following sections:
 * [Spring Boot Web](https://docs.spring.io/spring-boot/docs/current/reference/html/web.html)
 * [springdoc-openapi](https://springdoc.org/)
 * [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/3.1.3/reference/htmlsingle/index.html#using.devtools)
+* [Spring Security SAML2](https://docs.spring.io/spring-security/reference/servlet/saml2/login/overview.html)
 
 ### Guides
 
@@ -23,6 +24,7 @@ The following guides illustrate how to use some features concretely:
 * [Validating Form Input](https://spring.io/guides/gs/validating-form-input/)
 * [Documenting a Spring REST API Using OpenAPI 3.0](https://www.baeldung.com/spring-rest-openapi-documentation)
 * [gRPC-Spring-Boot-Starter Documentation](https://yidongnan.github.io/grpc-spring-boot-starter/en/)
+* [SAML with Spring Boot and Spring Security](https://www.baeldung.com/spring-security-saml)
 * [Technical Dokumentation](https://wiki.mgm-tp.com/confluence/x/dwMkEw)
 
 Healthchecks are available at:
@@ -40,40 +42,39 @@ http://localhost:8080/swagger-ui/index.html
 
 ## Run the application
 
-There are three profiles:
+There are two profiles:
 
 * default - The profile for production environment
-* dev - The profile for dev stages with connection to the info-manager
-* standalone - The profile for running a version of the application room where all external services are mocked.
+* local - The profile for dev stages with connection to the info-manager
 
 You can start the application by command line by using
 
 ```
-./gradlew bootRun --args='--spring.profiles.active=standalone'
+./mvnw spring-boot:run -Dspring-boot.run.profiles=local
 ```
 
 To change the local port of the management api you can use
 
 ```
-./gradlew bootRun --args='--management.server.port=8081'
+./mvnw spring-boot:run -Dmanagement.server.port=8081
 ```
 
 or with docker compose by using something like:
 
 ```
   ozg-applicationroom:
-    image: dockerregistry.mgm-tp.com/com.mgmtp.bup.ozg/ozg-antragsraum:latest
+    image: docker.ozg-sh.de/antragsraum-server:OZG-4803-0.1.0-SNAPSHOT
     platform: linux/amd64
     environment:
-      - SPRING_PROFILES_ACTIVE=dev
+      - SPRING_PROFILES_ACTIVE=local
       - MANAGEMENT_SERVER_PORT=8081
   ozg-info-manager:
-    image: dockerregistry.mgm-tp.com/com.mgmtp.bup.ozg/ozg-info-manager:latest
+    image: docker.ozg-sh.de/info-manager:OZG-4804-0.1.0-SNAPSHOT
     platform: linux/amd64
     environment:
       - SPRING_DATA_MONGODB_HOST=ozg-mongodb
-      - SPRING_PROFILES_ACTIVE=dev
-      - MANAGEMENT_SERVER_PORT=8082
+      - SPRING_PROFILES_ACTIVE=local
+      - MANAGEMENT_SERVER_PORT=8084
   ozg-mongodb:
     image: mongo:4
     ports:
@@ -84,33 +85,44 @@ or with docker compose by using something like:
       - mongodb-data:/data/db
 ```
 
-# Keycloak
+## Setting
 
-Build container locally (from within the _keycloak/_ directory):
+Important settings
 
-```shell
-docker build . -t ozg-keycloak --build-arg dockerRegistryPull=docker-repos.dockerregistry.mgm-tp.com
 ```
-
-Run that container on https://localhost:8443
-
-```shell
-docker run --env-file bup-db.env --name ozg-keycloak -p 8443:8443 ozg-keycloak
+grpc:
+  client:
+    info_manager:
+      address: static://127.0.0.1:9090  # The address of the InfoManager
+      negotiation-type: PLAINTEXT       # The negotiation type.
+clamav:
+    scanUrl: http://127.0.0.1:3000/api/v1/scan # The REST endpoint for virus scans
+ozgcloud:
+    code:
+        expire:
+            seconds: 30 # Time in seconds after the login code expires
+    jwt:
+        secret: "346593nbdgb8e74t6vw477q34bg83456§$%/&Hgvt78hlsjdgfw8äy.skeiw44tz asjkdefa wlfugwegw"
+        # Secret of signing the Jwt Token provided to the front end.
+    antragsraum:                
+        logoutSuccessUrl: "http://localhost:8082/?logout"  # The redirect url after a logout
+        authOrigins:            # the access-allow-oring setting for the authentication endpoints
+            - "http://localhost:8082"
+            - "http://localhost:8080"
+        apiOrigins:             # the access-allow-oring setting for the rest endpoints
+            - "http://localhost:8082"
+        otherOrigins:           # the access-allow-oring setting for other path like static files etc.
+            - "http://localhost:8082"
 ```
 
-with having a health check and metrics at:
-
-* https://localhost:8443/health
-* https://localhost:8443/metrics
+The setting regarding the BayernId configuration are documented
+here [BayernId Implementation and configuration](documentation/BayernIdLogin.md)
 
-Alternatively, you can directly
+Note:
+The InfoManager needs have the same value for the Jwt secret configured, so the InfoManager can verify the Tokens send
+in the gRPC metadata JWT_TOKEN field. Must be at least 256 bit long
 
-```shell
-cd keycloak/
-docker compose up
-```
-
-## Usefully Tools
+## Useful Tools
 
 ### grpcurl
 
@@ -148,16 +160,43 @@ curl -F file=@</path/to/the/file/file_name>  http://localhost:8080/api/file/6358
 
 #### Local Dev environment:
 
-| Port  | Application/Service                        | Addresses                                     |
-|-------|--------------------------------------------|-----------------------------------------------|
-| 8080  | Antragsraum REST Services and Swagger page | http://localhost:8080/swagger-ui/index.html#/ |
-| 8081  | Keycloak Admin interface                   | http://localhost:8081/admin/master/console    |
-| 8082  | Frontend                                   | http://localhost:8082/                        |
-| 8083  | Antragsraum Actuator                       | http://localhost:8083/actuator                |
-| 8084  | Infomanager Actuator                       | http://localhost:8084/actuator                |
-| 3310  | ClamAV                                     |                                               |
-| 9091  | Infomanager  GRPC Service                  |                                               |
-| 27027 | MongoDB                                    |                                               |
+| Port  | Application/Service       | Addresses                      |
+|-------|---------------------------|--------------------------------|
+| 8080  | Antragsraum REST Services | http://localhost:8080/         |
+| 8082  | Frontend                  | http://localhost:8082/         |
+| 8083  | Antragsraum Actuator      | http://localhost:8083/actuator |
+| 8084  | Infomanager Actuator      | http://localhost:8084/actuator |
+| 9091  | Infomanager GRPC Service  |                                |
+| 27027 | MongoDB                   |                                |
+
+Because the SAML Login cannot be used for a developer basic authentication has been added.
+
+To enable it the *local* profile must be used.
+This uses the settings defined in the application-local.yml file.
+Within this file following must be configured:
+
+```
+grpc:
+  client:
+    info_manager:
+      address: static://127.0.0.1:9091
+      negotiation-type: PLAINTEXT
+  ozg_service:
+    stubs:
+      enabled: true
+ozgcloud:
+  mock:
+    auth: true
+  grpc:
+    client:
+      negotiation-type: PLAINTEXT
+```
+
+Then you can use the user 'test' with the password 'test' to login.
+
+Whe using the local Profile the redirect url in the antragsraum-client must be configured to http://localhost:8080/login
 
+### More Information
 
+* [BayernId Implementation and configuration](documentation/BayernIdLogin.md)
 
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 6ca0dd1c962f2950c172e3de8c525e3cdf14b9af..0000000000000000000000000000000000000000
--- a/build.gradle
+++ /dev/null
@@ -1,78 +0,0 @@
-plugins {
-    id "java"
-    id "org.springframework.boot" version '3.1.5'
-    id "io.spring.dependency-management" version '1.1.3'
-    id "com.cinnober.gradle.semver-git" version "${semver_plugin}" apply false
-    id "jacoco"
-    id "org.sonarqube" version "${sonarqube_plugin}"
-    id "org.owasp.dependencycheck" version '8.4.2'
-    id "org.unbroken-dome.helm" version "${helm_plugin}"
-    id "org.unbroken-dome.helm-publish" version "${helm_plugin}"
-}
-
-java {
-    sourceCompatibility = '17'
-}
-
-jacocoTestReport {
-    reports {
-        xml.required = true
-    }
-}
-
-dependencyCheck {
-    formats = ['HTML', 'XML']
-}
-
-configurations {
-    compileOnly {
-        extendsFrom annotationProcessor
-    }
-}
-
-sourceSets {
-    integrationTest {
-        resources.srcDir("src/test/resources")
-    }
-}
-
-tasks.named('test') {
-    useJUnitPlatform()
-}
-
-bootJar {
-    manifest {
-        attributes 'Start-Class': 'de.mgm.bup.ozg.antragsraum.AntragsRaumApplication'
-    }
-}
-
-plugins.withType(JacocoPlugin) {
-    tasks["test"].finalizedBy 'jacocoTestReport'
-}
-
-allprojects {
-    ext.snapshotSuffix = '<count>.<sha>-SNAPSHOT'
-    apply plugin: 'com.cinnober.gradle.semver-git'
-
-    tasks.register("getVersion") {
-        description = "Prints the version of the project as calculated form the SemVer plugin."
-        println project.version
-    }
-}
-
-sonarqube {
-    properties {
-        property "sonar.projectKey", project.name
-        property "sonar.projectName", project.name
-        property "sonar.qualitygate.wait", "true"
-        property "sonar.projectBaseDir", "."
-        property "sonar.exclusions", "src/**/mocks/**"
-        // check is required (for Sonarqube Community Branch Plugin) commit, since the base project is not updated otherwise as merges into the master branch are treated like PRs
-        if (System.env.BRANCH_NAME != 'dev') {
-            property "sonar.pullrequest.key", System.env.BRANCH_NAME
-            property "sonar.pullrequest.branch", System.env.BRANCH_NAME
-            property "sonar.pullrequest.base", 'dev'
-        }
-    }
-}
-
diff --git a/clamAV/docker-compose.yml b/clamAV/docker-compose.yml
deleted file mode 100755
index ecfa3c529abf361bf37fce580f76d591d1b090bc..0000000000000000000000000000000000000000
--- a/clamAV/docker-compose.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-clamav:
-  container_name: clamav_container
-  ports:
-    - '3310:3310'
-  image: 'docker-repos.dockerregistry.mgm-tp.com/clamav/clamav:stable'
\ No newline at end of file
diff --git a/documentation/BayernIdLogin.md b/documentation/BayernIdLogin.md
new file mode 100644
index 0000000000000000000000000000000000000000..59ca4f478be0291489eeb4adb113d8b1b59cf5f7
--- /dev/null
+++ b/documentation/BayernIdLogin.md
@@ -0,0 +1,147 @@
+# BayernId
+
+## Einleitung:
+
+Der Antragsraum verwendet das Servicekonto im Bayerischen Portalverbund ( BayernId ) als Identity Provider und den
+Zugriff zu ermöglichen und abzusichern.
+
+Das Servicekonto im Bayerischen Portalverbund ( BayernId ) verwendet OASIS Security Assertion Markup Language (SAML)
+V2.0)
+https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf als Protokoll zur Authentifizierung des Benutzers.
+
+## Voraussetzungen:
+
+Damit der Antragsraum an die BayernId angeschlossen werden kann müssen SAML Metadaten erstellt werden und an
+Akdb übermittelt werden
+siehe [Dokumentation Servicekonto im Bayrischen Portalverbund v4.0](Das_Servicekonto_im_Bayerischen_Portalverbund_v4.0.pdf)
+
+Dazu müssen zwei Zertifikate mit privaten Keys erstellt werden:
+
+Beispiel:
+
+```
+openssl req -newkey rsa:2048 -nodes -keyout bayernid-test-enc.key -x509 -days 365 -out bayernid-test-enc.crt
+openssl req -newkey rsa:2048 -nodes -keyout bayernid-test-sign.key -x509 -days 365 -out bayernid-test-sign.crt
+```
+
+Diese müssen dann in das [Metadaten Template](ozg-application-room-saml-metadata-template.xml) eingefügt werden.
+
+Der Text zwischen den Markierungen "-----BEGIN CERTIFICATE-----" und "-----END CERTIFICATE-----" des Zertifikates
+bayernid-test-sign.crt wird an der Stelle "<!-- signing certificate here -->" eingefügt.
+
+Der Text zwischen den Markierungen "-----BEGIN CERTIFICATE-----" und "-----END CERTIFICATE-----" des Zertifikates
+bayernid-test-enc.crt wird an der Stelle "<!-- encryption certificate here -->" eingefügt.
+
+Es müssen auch weitere Informationen in das Template eingetragen werden:
+
+* AssertionConsumerService Location. Das ist die öffentlich erreichbare Adresse des Spring SAML2 SSO Endpunkts des
+  Antragraum-Backends. Ist für das Binding HTTP-POST und HTTP-Redirect gleich.
+* ContactPerson contactType="technical": Hier muss die eMail Adresse des technischen Ansprechpartners eingetragen
+  werden.
+
+Eingetragen sind schon die Werte:
+
+* entityID: Ist schon eingetragen und lautet: https://antragsraum.ozgcloud.de/
+* ContactPerson contactType="support: Ansprechpartner für die Fachanwendung eMail: ozg-cloud-admin3@mgm-tp.com
+* Organization: Hier ist mgm tp eingetragen
+
+Es ist darauf zu achten, dass
+
+1. die einzureichenden Metadaten kein Ablaufdatum enthalten ( validUntil-Attribut )
+2. die Metadaten kein ID-Attribut enthalten,
+3. die entityID als URI in URL-Notation mit https-Protokoll-Prefix ohne(!) Portnummer anzugeben ist. Diese muss nicht(!)
+   zwingend mit der
+   tatsächlich genutzten URL-Domain übereinstimmen, ist aber in Zusammenhang mit dem Attribut bPK in dieser Notation
+   anzugeben. Die Auswahl
+   der entityID kann nach Aufnahme in den Wirkbetrieb nicht(!) mehr verändert werden, und sollte daher den Betreiber der
+   Drittanwendung eindeutig
+   identifizieren (also nicht unspezifisch sein) wie folgendes
+   Negativbeispiel (!) https://drittanwendung.com:1234/serviceprovider , sondern sprechend
+   wie folgendes Vorbild https://dorf-enigmatting.de/online-rathaus
+
+Die ausgefüllte Metadaten Datei muss dann an Adkb (BayernID@akdb.de) gesendet werden.
+Siehe dazu 7. Betriebsvoraussetzungen in
+der [Dokumentation Servicekonto im Bayrischen Portalverbund v4.0](Das_Servicekonto_im_Bayerischen_Portalverbund_v4.0.pdf)
+
+## Ablauf
+
+Der Ablauf des Logins über das BayernId Portals ist:
+
+![Ablauf login](diagrams/SAML-login-sequence.png)
+
+Wenn sich der Endkunde im Antragsraum anmeldet, erfolgt ein Aufruf des login-Endpunkts im Antragsraum-Backends.
+Von dort wird er auf die Login-Seite des BayernId Portals weiter gleitet wo er sich Anmelden kann.
+
+War die Anmeldung erfolgreich erfolgt eine Weiterleitung zum SAML SSO Login Endpunkts im Backend mit der SAMLResponse.
+Diese wird im Backend ausgewertet und es erfolgt ein Redirect auf den Client mit dem Login code.
+Mit dem Login code wird sein Request an das Backend gemacht und wenn der Code erfolreich geprüft wurde wird ein
+Jwt Token mit den Benutzerdaten an den Client gesendet und er ist jetzt erfolgreich angemeldet.
+
+## Parameters
+
+Um die BayernId zu konfigurieren gibt es in der application.yml folgenden Abschnitt:
+
+```
+ozgcloud:
+    bayernid:
+        authnMethods:  # Die möglichen Authentifizieungsmethoden
+            Authega: "false"
+            Benutzername: "true"
+            eID: "false"
+            eIDAS: "false"
+            Diia: "false"
+            Elster: "true"
+            FINK: "false"
+        requestedAttributeUrns: # Die von dem BayernId Portal zurückgegeben Informationen
+            - "urn:oid:2.5.4.18"                    # Postkorb-Handle = die ID des Postfachs des Benutzers
+            - "urn:oid:1.3.6.1.4.1.25484.494450.3"  # bereichsspezifisches Personenkennzeichen 2 ? eigentlichen ssPIN" (siehe Kapitel zur bPK) im Servicedokument 
+            - "urn:oid:1.2.40.0.10.2.1.1.261.94"    # Vertrauensniveau (siehe Kapitel 6.2.3 Vertrauensniveau)
+            - "urn:oid:1.3.6.1.4.1.25484.494450.2"  # AssertionProvedBy (siehe Kapitel 6.2.5 AssertionProvedBy 
+            - "urn:oid:2.5.4.42"                    # Vorname
+            - "urn:oid:2.5.4.4"                     # Nachname
+            - "urn:oid:0.9.2342.19200300.100.1.3"   # Emailadresse
+        redirect-url: "http://localhost:8082"       # Die öffentliche Adresse des Antragraum-Clients
+spring:
+  .
+  .
+  .
+  security:
+    saml2:
+      relyingparty:
+        registration:
+          bayernid:
+            entity-id: https://antragsraum.ozgcloud.de/                     # Die festgelegte entityId
+            signing:
+              credentials:
+                - private-key-location: "classpath:bayernid-test-sign.key"  # Der private Key des Signging Zertifikats (! Nicht veröffenlichen)
+                  certificate-location: "classpath:bayernid-test-sign.crt"  # Das Signging Zertifikat wie im Metadaten Dokument an Akdb gesendet 
+            decryption:
+              credentials:
+                - private-key-location: "classpath:bayernid-test-enc.key"   # Der private Key des Encyption Zertifikats (! Nicht veröffenlichen)
+                  certificate-location: "classpath:bayernid-test-enc.crt"   # Das Encyption Zertifikat wie im Metadaten Dokument an Akdb gesendet
+            assertingparty:
+              singlesignon:
+                sign-request: true
+              metadata-uri: "classpath:/metadata/bayernid-idp-infra.xml"    # Das idp Metadaten Dokument kann z.b. von https://infra-pre-id.bayernportal.de/idp geladen werden 
+```
+
+### Umgebungen
+
+Es gibt folgendende BayernId Umgebungen:
+
+* pre-a-id.bayernportal.de
+* pre-b-id.bayernportal.de
+* pre-c-id.bayernportal.de
+* pre-d-id.bayernportal.de
+* pre-e-id.bayernportal.de
+* infra-pre-id.bayernportal.de -- wird for DEV verwendet
+* pre-id.bayernportal.de
+* id.bayernportal.de -- wird für PROD verwendet
+
+Das Einspielen der MetaDaten auf DEV erledigt Akdb auf Kosten des Landes Bayern, auf PROD hingegen musses von mgm
+bezahlt werden.
+
+## Implementierung
+
+Es wird das Spring Framework verwendet, um den Login zu implementieren.
+Siehe [SAML 2.0 Login Overview](https://docs.spring.io/spring-security/reference/servlet/saml2/login/overview.html)
\ No newline at end of file
diff --git a/documentation/Das Servicekonto im Bayerischen Portalverbund v4.0.pdf b/documentation/Das_Servicekonto_im_Bayerischen_Portalverbund_v4.0.pdf
similarity index 100%
rename from documentation/Das Servicekonto im Bayerischen Portalverbund v4.0.pdf
rename to documentation/Das_Servicekonto_im_Bayerischen_Portalverbund_v4.0.pdf
diff --git a/documentation/diagrams/SAML-login-sequence.png b/documentation/diagrams/SAML-login-sequence.png
new file mode 100644
index 0000000000000000000000000000000000000000..42eaa3cf7f22c3c9b5f4172b8daabe7b44eb62bc
Binary files /dev/null and b/documentation/diagrams/SAML-login-sequence.png differ
diff --git a/documentation/diagrams/SAML-login-sequence.puml b/documentation/diagrams/SAML-login-sequence.puml
new file mode 100644
index 0000000000000000000000000000000000000000..f477329bf5ef064787c7403dd6a3ce29e25ac0dc
--- /dev/null
+++ b/documentation/diagrams/SAML-login-sequence.puml
@@ -0,0 +1,15 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+Antragsraum_Client -> Antragsraum_Backend: Login Request
+Antragsraum_Backend <-> BayernId_Portal: Request SAML2 SSO Service
+Antragsraum_Client <-- Antragsraum_Backend: Redirect to SSO Login Page
+Antragsraum_Client --> BayernId_Portal: User login to SSO Provider
+BayernId_Portal -> Antragsraum_Backend: SAML2 Authorization Response
+Antragsraum_Backend -> Antragsraum_Client: Login Code Response
+Antragsraum_Client -> Antragsraum_Backend: Authorization Request
+Antragsraum_Backend -> Antragsraum_Client: Authorization Response
+
+@enduml
\ No newline at end of file
diff --git a/documentation/ozg-application-room-dev-saml-metadata.xml b/documentation/ozg-application-room-dev-saml-metadata.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1e9cbc33ebebb919dfd61e87c9c31083d9233d9b
--- /dev/null
+++ b/documentation/ozg-application-room-dev-saml-metadata.xml
@@ -0,0 +1,96 @@
+<md:EntityDescriptor entityID="https://antragsraum.ozgcloud.de/" 
+						xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
+						xmlns:ds="http://www.w3.org/2000/09/xmldsig#" >
+    <md:SPSSODescriptor AuthnRequestsSigned="true"
+                        WantAssertionsSigned="true"
+                        protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+        <md:Extensions>
+            <mdui:UIInfo xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
+                <mdui:DisplayName xml:lang="de">Antragsraum</mdui:DisplayName>
+                <mdui:Description xml:lang="de">Die Anwendung Antragsraum ermöglicht es den Bürgern auf Anfragen von Behörden zu antworten.</mdui:Description>
+                <mdui:InformationURL xml:lang="de">https://dev.antragsraum.de/imprint</mdui:InformationURL>
+                <mdui:PrivacyStatementURL xml:lang="de">https://dev.antragsraum.de/privacy-policy</mdui:PrivacyStatementURL>
+            </mdui:UIInfo>
+        </md:Extensions>
+        <md:KeyDescriptor use="signing">
+          <ds:KeyInfo>
+            <ds:X509Data>
+              <ds:X509Certificate>
+    MIIEGzCCAwOgAwIBAgIUPBZDVDRuGt0H15FLy5JUzcDlmn4wDQYJKoZIhvcNAQEL
+    BQAwgZwxCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcx
+    EjAQBgNVBAcMCVN0dXR0Z2FydDEhMB8GA1UECgwYbWdtIHRlY2hub2xvZ2llIHBh
+    cnRuZXJzMRIwEAYDVQQLDAlvemctY2xvdWQxJTAjBgkqhkiG9w0BCQEWFmplbnMu
+    cmVlc2VAZ21nbS10cC5jb20wHhcNMjQwMzIwMDc0NzE0WhcNMjUwMzIwMDc0NzE0
+    WjCBnDELMAkGA1UEBhMCREUxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzES
+    MBAGA1UEBwwJU3R1dHRnYXJ0MSEwHwYDVQQKDBhtZ20gdGVjaG5vbG9naWUgcGFy
+    dG5lcnMxEjAQBgNVBAsMCW96Zy1jbG91ZDElMCMGCSqGSIb3DQEJARYWamVucy5y
+    ZWVzZUBnbWdtLXRwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+    AKIZTQQ9CLos2JZ8Vmx2dIicsWlf6P5Br1IRf4i/CrekiiQGJaXnm0lMaL0mJ2ON
+    pyPxyctYtuxn0G8sdtLTGWC7fsCR4noe08wr0ufphkjXq24l/LPzdY1RM2n3PAml
+    VTmVg975T3joIU2xHHDFOAmDTDVUYBaCe984AnJgFQM4SO1hsPYLD5Y4otLiChAe
+    JRrqxLWc/49oxGkezgiOYo3qJ0y+m2okM//R5qcDxAAH2InyA1L7EX8gb7/bFN96
+    xCXwhj1U7vW8ltFlFeCpU/gvXLPkSq/298EXiJZSqRTXXr0QQOyo6r0UH4FqFeBk
+    wXs8CTfaAqMEsCRuf6YVY8sCAwEAAaNTMFEwHQYDVR0OBBYEFOqZxTtPv4lPYnch
+    tvCAmzU7JuguMB8GA1UdIwQYMBaAFOqZxTtPv4lPYnchtvCAmzU7JuguMA8GA1Ud
+    EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACT/DASqepWxL2VCgbDS2v1G
+    L/utCUUgybKvOA393vRq2NLgGxeb58U3KBeiv2MFhHHy0TEmK50inPFL1aB6HEwZ
+    10zi74WgRY/G+lfPUtORiAvm6dbQn+jhRFuJkHohb7VJuGwI9Q4EqeDY6tINOgHu
+    vuUrIyADWva/DF1rvVT7rgustKiPRRaOFNJMqhBb+458CfwrYWpUvbWDtTKpInmL
+    7wmzEPIoebYlLD4EDPZ8sDLPbP2hQM4geuPorBybGjTDYWsXU3zCZNv2PLnmp61F
+    dcuDpnS9e+ZRVFVDJpmL9mWrsh7iJ+P9A7x3MQP4gRXe4yBMaGw1ExIjhEfkMac=
+          </ds:X509Certificate>
+            </ds:X509Data>
+          </ds:KeyInfo>
+        </md:KeyDescriptor>
+        <md:KeyDescriptor use="encryption">
+          <ds:KeyInfo>
+            <ds:X509Data>
+              <ds:X509Certificate>
+    MIIEGzCCAwOgAwIBAgIUWPZFfhB4+iI3XdjUTMqhhDkljGgwDQYJKoZIhvcNAQEL
+    BQAwgZwxCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcx
+    EjAQBgNVBAcMCVN0dXR0Z2FydDEhMB8GA1UECgwYbWdtIHRlY2hub2xvZ2llIHBh
+    cnRuZXJzMRIwEAYDVQQLDAlvemctY2xvdWQxJTAjBgkqhkiG9w0BCQEWFmplbnMu
+    cmVlc2VAZ21nbS10cC5jb20wHhcNMjQwMzIwMDc0MDA5WhcNMjUwMzIwMDc0MDA5
+    WjCBnDELMAkGA1UEBhMCREUxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzES
+    MBAGA1UEBwwJU3R1dHRnYXJ0MSEwHwYDVQQKDBhtZ20gdGVjaG5vbG9naWUgcGFy
+    dG5lcnMxEjAQBgNVBAsMCW96Zy1jbG91ZDElMCMGCSqGSIb3DQEJARYWamVucy5y
+    ZWVzZUBnbWdtLXRwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+    ANogQ1D22S1V53sAch82/LvbbqjMUQWCNOAyUEzrbEW0SqJ3ED+93ZL0rTwstiAj
+    XQzPydKmo6keHlexm4f3EfBgJzUG6Y0O8BL/GG02n2ZaXZa3rtbY1y7CSBgICUGe
+    9QPmHADUqTkzXwUVuKf6Ie1uyEbqLTr5T5PGOcESsQxVFkHG6/i2H7QhoeLDAWw5
+    2ENwDRigM/mDaMliI5TWmM4T8DxKLZ7FUiQGDt/7vpQdBs+vit2ndaoQvQbpraBd
+    /KVsbB3epXXFFX/y37+/lHMYtkCnPvHQljYjBz1hH6zcf1VcJLrmSElXHK74HLl5
+    D/xYpUCCQX8EU0YIbPULejMCAwEAAaNTMFEwHQYDVR0OBBYEFFfqF7V0PscLpeAx
+    Vj3ADkWSftbnMB8GA1UdIwQYMBaAFFfqF7V0PscLpeAxVj3ADkWSftbnMA8GA1Ud
+    EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAK70r5o4oLPu5JXJmKWnI7CD
+    wjZR0XQX8x1+tWtqT/v6Trz4p6SGxdPzA+Z9dKl5TrHWn0Jue79NCTQO1fgn/L5Q
+    ZblOCxFhe+yvgeqyMPRHtlF1RicMn+yPwS3QKON0INmsch64IVXJZgJms0d7HRcF
+    GAn644FdxZH9IX39eqs1Y7l1Ac++4O9uSiB6N+js2ZTOI+KDrvVhKblE+0ehx3bM
+    +hqsXpRE6iq9wD1wAGiMxMTetG1kI0PMgDiDXTfG3ZkvpYtTyU2Mkl+F9FFWhwGI
+    LrLKJeLZRRpwkDvWNUpER5UveXJvY8TKV8HZDhEzWB3IAjRYufHnP5MHLgMZmXk=
+          </ds:X509Certificate>
+            </ds:X509Data>
+          </ds:KeyInfo>
+        </md:KeyDescriptor>
+        <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
+        <md:AssertionConsumerService
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+            Location="https://dev.antragsraum.de/login/saml2/sso/bayernid" index="1"/>
+        <md:AssertionConsumerService
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+            Location="https://dev.antragsraum.de/login/saml2/sso/bayernid" index="2"/>
+  </md:SPSSODescriptor>
+  <md:Organization>
+       <md:OrganizationName xml:lang="de-DE">mgm technology partners GmbH</md:OrganizationName>
+       <md:OrganizationDisplayName xml:lang="de-DE">mgm technology partners GmbH</md:OrganizationDisplayName>
+       <md:OrganizationURL xml:lang="de-DE">https://www.mgm-tp.com/</md:OrganizationURL>
+    </md:Organization>
+    <md:ContactPerson contactType="technical">
+        <md:GivenName>technischer Ansprechpartner</md:GivenName>
+        <md:EmailAddress>jens.reese@mgm-tp.com</md:EmailAddress>
+    </md:ContactPerson>
+	<md:ContactPerson contactType="support">
+        <md:GivenName>Ansprechpartner für die Fachanwendung</md:GivenName>
+        <md:EmailAddress>ozg-cloud-admin3@mgm-tp.com</md:EmailAddress>
+    </md:ContactPerson>
+</md:EntityDescriptor>
diff --git a/documentation/ozg-application-room-saml-metadata-template.xml b/documentation/ozg-application-room-saml-metadata-template.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6d3251ab18971fbb14112e41e7bd3895308634e0
--- /dev/null
+++ b/documentation/ozg-application-room-saml-metadata-template.xml
@@ -0,0 +1,53 @@
+<md:EntityDescriptor entityID="https://antragsraum.ozgcloud.de/"
+						xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
+						xmlns:ds="http://www.w3.org/2000/09/xmldsig#" >
+    <md:SPSSODescriptor AuthnRequestsSigned="true"
+                        WantAssertionsSigned="true"
+                        protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+    <md:KeyDescriptor use="signing">
+      <ds:KeyInfo>
+        <ds:X509Data>
+          <ds:X509Certificate>
+<!-- signing certificate here -->
+		  </ds:X509Certificate>
+        </ds:X509Data>
+      </ds:KeyInfo>
+    </md:KeyDescriptor>
+    <md:KeyDescriptor use="encryption">
+      <ds:KeyInfo>
+        <ds:X509Data>
+          <ds:X509Certificate>
+<!-- encryption certificate here -->
+		  </ds:X509Certificate>
+        </ds:X509Data>
+      </ds:KeyInfo>
+    </md:KeyDescriptor>
+    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
+    <md:AssertionConsumerService
+		Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" 
+		Location="public address of antragsraum spring saml sso login" index="1"/>
+    <md:AssertionConsumerService
+		Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" 
+		Location="public address of antragsraum spring saml sso login" index="2"/>
+  </md:SPSSODescriptor>
+  <md:Organization>
+       <md:OrganizationName xml:lang="de-DE">mgm technology partners GmbH</md:OrganizationName>
+       <md:OrganizationDisplayName xml:lang="de-DE">mgm technology partners GmbH</md:OrganizationDisplayName>
+       <md:OrganizationURL xml:lang="de-DE">https://www.mgm-tp.com/</md:OrganizationURL>
+    </md:Organization>
+    <md:ContactPerson contactType="technical">
+        <md:GivenName>technischer Ansprechpartner</md:GivenName>
+        <md:EmailAddress>ansprechpartner</md:EmailAddress>
+    </md:ContactPerson>
+	<md:ContactPerson contactType="support">
+        <md:GivenName>Ansprechpartner für die Fachanwendung</md:GivenName>
+        <md:EmailAddress>ozg-cloud-admin3@mgm-tp.com</md:EmailAddress>
+    </md:ContactPerson>
+    <md:Extensions>
+        <mdui:UIInfo xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
+            <mdui:DisplayName xml:lang="de">Antragsraum</mdui:DisplayName>
+            <mdui:InformationURL xml:lang="de">https://dev.antragsraum.de/imprint</mdui:InformationURL>
+            <mdui:PrivacyStatementURL xml:lang="de">https://dev.antragsraum.de/privacy-policy</mdui:PrivacyStatementURL>
+        </mdui:UIInfo>
+    </md:Extensions>
+</md:EntityDescriptor>
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 033e24c4cdf41af1ab109bc7f253b2b887023340..0000000000000000000000000000000000000000
Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 9f4197d5f4b9fdf163daa4fe7f17423f8aac7aca..0000000000000000000000000000000000000000
--- a/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
-networkTimeout=10000
-validateDistributionUrl=true
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
deleted file mode 100755
index fcb6fca147c0cd248611f3d67b8d74b470402795..0000000000000000000000000000000000000000
--- a/gradlew
+++ /dev/null
@@ -1,248 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-##############################################################################
-#
-#   Gradle start up script for POSIX generated by Gradle.
-#
-#   Important for running:
-#
-#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-#       noncompliant, but you have some other compliant shell such as ksh or
-#       bash, then to run this script, type that shell name before the whole
-#       command line, like:
-#
-#           ksh Gradle
-#
-#       Busybox and similar reduced shells will NOT work, because this script
-#       requires all of these POSIX shell features:
-#         * functions;
-#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-#         * compound commands having a testable exit status, especially «case»;
-#         * various built-in commands including «command», «set», and «ulimit».
-#
-#   Important for patching:
-#
-#   (2) This script targets any POSIX shell, so it avoids extensions provided
-#       by Bash, Ksh, etc; in particular arrays are avoided.
-#
-#       The "traditional" practice of packing multiple parameters into a
-#       space-separated string is a well documented source of bugs and security
-#       problems, so this is (mostly) avoided, by progressively accumulating
-#       options in "$@", and eventually passing that to Java.
-#
-#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-#       see the in-line comments for details.
-#
-#       There are tweaks for specific operating systems such as AIX, CygWin,
-#       Darwin, MinGW, and NonStop.
-#
-#   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-#       within the Gradle project.
-#
-#       You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
-    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
-    [ -h "$app_path" ]
-do
-    ls=$( ls -ld "$app_path" )
-    link=${ls#*' -> '}
-    case $link in             #(
-      /*)   app_path=$link ;; #(
-      *)    app_path=$APP_HOME$link ;;
-    esac
-done
-
-# This is normally unused
-# shellcheck disable=SC2034
-APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
-    echo "$*"
-} >&2
-
-die () {
-    echo
-    echo "$*"
-    echo
-    exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in                #(
-  CYGWIN* )         cygwin=true  ;; #(
-  Darwin* )         darwin=true  ;; #(
-  MSYS* | MINGW* )  msys=true    ;; #(
-  NONSTOP* )        nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-        # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD=$JAVA_HOME/jre/sh/java
-    else
-        JAVACMD=$JAVA_HOME/bin/java
-    fi
-    if [ ! -x "$JAVACMD" ] ; then
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-else
-    JAVACMD=java
-    if ! command -v java >/dev/null 2>&1
-    then
-        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
-    case $MAX_FD in #(
-      max*)
-        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
-        # shellcheck disable=SC3045
-        MAX_FD=$( ulimit -H -n ) ||
-            warn "Could not query maximum file descriptor limit"
-    esac
-    case $MAX_FD in  #(
-      '' | soft) :;; #(
-      *)
-        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
-        # shellcheck disable=SC3045
-        ulimit -n "$MAX_FD" ||
-            warn "Could not set maximum file descriptor limit to $MAX_FD"
-    esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-#   * args from the command line
-#   * the main class name
-#   * -classpath
-#   * -D...appname settings
-#   * --module-path (only if needed)
-#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
-    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
-    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
-    JAVACMD=$( cygpath --unix "$JAVACMD" )
-
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    for arg do
-        if
-            case $arg in                                #(
-              -*)   false ;;                            # don't mess with options #(
-              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
-                    [ -e "$t" ] ;;                      #(
-              *)    false ;;
-            esac
-        then
-            arg=$( cygpath --path --ignore --mixed "$arg" )
-        fi
-        # Roll the args list around exactly as many times as the number of
-        # args, so each arg winds up back in the position where it started, but
-        # possibly modified.
-        #
-        # NB: a `for` loop captures its iteration list before it begins, so
-        # changing the positional parameters here affects neither the number of
-        # iterations, nor the values presented in `arg`.
-        shift                   # remove old arg
-        set -- "$@" "$arg"      # push replacement arg
-    done
-fi
-
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Collect all arguments for the java command;
-#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-#     shell script including quotes and variable substitutions, so put them in
-#     double quotes to make sure that they get re-expanded; and
-#   * put everything else in single quotes, so that it's not re-expanded.
-
-set -- \
-        "-Dorg.gradle.appname=$APP_BASE_NAME" \
-        -classpath "$CLASSPATH" \
-        org.gradle.wrapper.GradleWrapperMain \
-        "$@"
-
-# Stop when "xargs" is not available.
-if ! command -v xargs >/dev/null 2>&1
-then
-    die "xargs is not available"
-fi
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-#   set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
-        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
-        xargs -n1 |
-        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
-        tr '\n' ' '
-    )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
deleted file mode 100644
index 93e3f59f135dd2dd498de4beb5c64338cc33beeb..0000000000000000000000000000000000000000
--- a/gradlew.bat
+++ /dev/null
@@ -1,92 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem      https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem  Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/keycloak/Dockerfile b/keycloak/Dockerfile
deleted file mode 100644
index ac1ca2bbf3353d83d6d1d8cbfebf7c7b1ae10a68..0000000000000000000000000000000000000000
--- a/keycloak/Dockerfile
+++ /dev/null
@@ -1,24 +0,0 @@
-ARG dockerRegistryPull
-FROM ${dockerRegistryPull}/keycloak/keycloak:22.0.4 as builder
-
-# # Enable health and metrics support
-ENV KC_HEALTH_ENABLED=true
-ENV KC_METRICS_ENABLED=true
-
-# Configure a database vendor
-ENV KC_DB=postgres
-
-# copy bayernid plugin to plugin directory
-COPY bayernid-plugin-1.0.1-SNAPSHOT.jar /opt/keycloak/standalone/deployments/plugin.jar
-
-WORKDIR /opt/keycloak
-# # for demonstration purposes only, please make sure to use proper certificates in production instead
-RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=ozg-keycloak" -alias server -ext "SAN:c=DNS:localhost,DNS:ozg-keycloak,IP:127.0.0.1" -keystore conf/server.keystore
-RUN /opt/keycloak/bin/kc.sh build
-
-FROM ${dockerRegistryPull}/keycloak/keycloak:22.0.4
-COPY --from=builder /opt/keycloak/ /opt/keycloak/
-
-# ENV KC_DB=postgres
-
-ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
\ No newline at end of file
diff --git a/keycloak/JenkinsBuildFile b/keycloak/JenkinsBuildFile
deleted file mode 100644
index 5c75d37fec0a27a62555588d69098ff39252cf1f..0000000000000000000000000000000000000000
--- a/keycloak/JenkinsBuildFile
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env groovy
-
-pipeline {
-    agent { label 'linux-node' }
-    options {
-        ansiColor('xterm')
-        disableConcurrentBuilds()
-        buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5'))
-    }
-    tools {
-        gradle 'Gradle8'
-        jdk 'OpenJDK17'
-    }
-    environment {
-        GRADLE_USER_HOME = "${WORKSPACE}/.gradle"
-    }
-
-    stages {
-        stage('Build Image') {
-            steps {
-                script {
-                    buildEnvironment {
-                        sh 'gradle :ozg-keycloak:buildImage'
-                    }
-                }
-            }
-        }
-
-        stage('Push Image') {
-            steps {
-                script {
-                    buildEnvironment {
-                        currentBuild.displayName = getBuildName()
-                        sh 'gradle :ozg-keycloak:pushImage'
-                    }
-                }
-            }
-        }
-
-        stage('Publish Helm Chart'){
-            steps{
-                script{
-                    buildEnvironment{
-                        sh 'gradle :ozg-keycloak:helmPublish'
-                    }
-                }
-            }
-        }
-    }
-
-    post {
-        unsuccessful {
-            emailext body: "${currentBuild.currentResult}: Job ${env.JOB_NAME} build ${env.BUILD_NUMBER}\nMore info at: ${env.BUILD_URL}",
-                recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']],
-                subject: "[Jenkins] ozg-keycloak build ${currentBuild.currentResult}: Job ${env.JOB_NAME}"
-        }
-    }
-}
-
-String getBuildName() {
-    String projectVersion = sh(script: 'gradle :getVersion -q', returnStdout: true)
-    return projectVersion.trim()
-}
-
-void buildEnvironment(Closure body) {
-    withCredentials([
-        usernamePassword(// for the maven repository
-            credentialsId: 'ci-user',
-            usernameVariable: 'repository_username',
-            passwordVariable: 'repository_password'),
-        usernamePassword( // for the docker builder repository
-            credentialsId: 'ci-user-nexus',
-            usernameVariable: 'repository_pull_username',
-            passwordVariable: 'repository_pull_password'),
-        usernamePassword(// for the docker publish repository
-            credentialsId: 'ci-user-nexus',
-            usernameVariable: 'repository_publish_username',
-            passwordVariable: 'repository_publish_password'),
-        usernamePassword(// for the helm repository
-            credentialsId: 'ci-user-nexus',
-            usernameVariable: 'repository_helm_username',
-            passwordVariable: 'repository_helm_password')
-
-    ]) {
-        configFileProvider([configFile(fileId: 'gradle-settings', targetLocation: "${GRADLE_USER_HOME}/init.d/settings.gradle")]) {
-            nodejs(configId: 'npmrc', nodeJSInstallationName: 'Node18') {
-                body()
-            }
-        }
-    }
-}
diff --git a/keycloak/bayernid-plugin-1.0.1-SNAPSHOT.jar b/keycloak/bayernid-plugin-1.0.1-SNAPSHOT.jar
deleted file mode 100644
index 099ba3571ef8dffe8e1fd6e9a911eabac6ec1042..0000000000000000000000000000000000000000
Binary files a/keycloak/bayernid-plugin-1.0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/keycloak/build.gradle b/keycloak/build.gradle
deleted file mode 100644
index afb40a692fd51555bff5519768a0d4227d60d36e..0000000000000000000000000000000000000000
--- a/keycloak/build.gradle
+++ /dev/null
@@ -1,63 +0,0 @@
-plugins {
-    id 'com.bmuschko.docker-remote-api' version "${docker_remote_plugin}"
-    id "org.unbroken-dome.helm" version "${helm_plugin}"
-    id "org.unbroken-dome.helm-publish" version "${helm_plugin}"
-}
-
-import com.bmuschko.gradle.docker.tasks.image.*
-
-def dockerTags = ["${dockerRegistryForPublish}/${project.group}/${project.name}:${project.version}"]
-def isDev = 'git rev-parse --abbrev-ref HEAD'.execute().text.trim() == 'dev'
-if (isDev) {
-    dockerTags.add("${dockerRegistryForPublish}/${project.group}/${project.name}:latest")
-}
-
-tasks.register('buildImage', DockerBuildImage) {
-    description = "Builds a Docker Image for the Keycloak."
-    inputDir = file(".")
-    buildArgs = [dockerRegistryPull: "$dockerRegistryPull"]
-    images.addAll(dockerTags)
-    registryCredentials {
-        url = dockerRegistryPull
-		username = project.findProperty("repository_pull_username") ?: System.getenv('repository_pull_username')
-		password = project.findProperty("repository_pull_password") ?: System.getenv('repository_pull_password')
-    }
-}
-
-tasks.register('pushImage', DockerPushImage) {
-    description = "Pushes the Keycloak Docker Image to the Artifactory."
-    images.addAll(dockerTags)
-    registryCredentials {
-        url = dockerRegistryForPublish
-		username = project.findProperty("repository_publish_username") ?: System.getenv('repository_publish_username')
-		password = project.findProperty("repository_publish_password") ?: System.getenv('repository_publish_password')
- 
-    }
-}
-
-sonarqube {
-    skipProject = true
-}
-helm {
-    filtering{
-        values.put "namespace", findProperty("namespace")
-        values.put "appName",  project.name
-    }
-
-    publishing {
-        repositories {
-            nexus {
-                url = uri("${helmRepositoryHost}")
-                repository = isSnapshot() ? "${helmRepositorySnapshot}" : "${helmRepositoryRelease}"
-                apiVersion = 'v1'
-                credentials {
-                    username = project.findProperty("repository_helm_username") ?: System.getenv('repository_publish_username') 
-                    password = project.findProperty("repository_helm_password") ?: System.getenv('repository_publish_password')
-                }
-            }
-        }
-    }
-}
-boolean isSnapshot( ){
-    project.version.matches(".*SNAPSHOT.*")
-}
diff --git a/keycloak/bup-db.env b/keycloak/bup-db.env
deleted file mode 100644
index 6242a4e96fc85330fb29be977389a78458211a16..0000000000000000000000000000000000000000
--- a/keycloak/bup-db.env
+++ /dev/null
@@ -1,7 +0,0 @@
-KEYCLOAK_ADMIN=admin
-KEYCLOAK_ADMIN_PASSWORD=change_me
-KC_DB_URL=jdbc:postgresql://bup-db.mgm-edv.de:5432/ozg_keycloak
-KC_DB_USERNAME=ozg_keycloak
-KC_DB_PASSWORD=zvZWKMOzJB8MEPg8o2dJ
-KC_HOSTNAME=localhost
-KC_DB=postgres
\ No newline at end of file
diff --git a/keycloak/docker-compose.yml b/keycloak/docker-compose.yml
deleted file mode 100644
index 54e368390dc1aaa6ce81673c710472cfdf477099..0000000000000000000000000000000000000000
--- a/keycloak/docker-compose.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-version: '3'
-services:
-  keycloak:
-    image: ozg-keycloak
-    build:
-      context: .
-      dockerfile: Dockerfile
-      args:
-        dockerRegistryPull: docker-repos.dockerregistry.mgm-tp.com
-    env_file:
-      - ./bup-db.env
-    ports:
-      - 8443:8443
\ No newline at end of file
diff --git a/keycloak/src/main/helm/Chart.yaml b/keycloak/src/main/helm/Chart.yaml
deleted file mode 100755
index 6bd1a032a494c479039382440464d270eadc20ba..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/Chart.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-apiVersion: v2
-name: GETS_REPLACED_BY_HELM_PLUGIN
-description: A Helm chart for Kubernetes
-
-# A chart can be either an 'application' or a 'library' chart.
-#
-# Application charts are a collection of templates that can be packaged into versioned archives
-# to be deployed.
-#
-# Library charts provide useful utilities or functions for the chart developer. They're included as
-# a dependency of application charts to inject those utilities and functions into the rendering
-# pipeline. Library charts do not define any templates and therefore cannot be deployed.
-type: application
-
-# This is the chart version. This version number should be incremented each time you make changes
-# to the chart and its templates, including the app version.
-# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: "${chartVersion}"
-
-# This is the version number of the application being deployed. This version number should be
-# incremented each time you make changes to the application. Versions are not expected to
-# follow Semantic Versioning. They should reflect the version the application is using.
-# It is recommended to use it with quotes.
-appVersion: "${chartVersion}"
diff --git a/keycloak/src/main/helm/templates/02_secrets.yml b/keycloak/src/main/helm/templates/02_secrets.yml
deleted file mode 100755
index f6067268a7ff08144c8b6667010fa92141377dae..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/templates/02_secrets.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-apiVersion: v1
-kind: Secret
-metadata:
-  name: postgres-credentials
-  namespace: {{ .Values.global.namespace }}
-type: Opaque
-stringData:
-  POSTGRES_USER: {{ .Values.postgres.user }}
-  POSTGRES_PASSWORD: {{ .Values.postgres.password }}
-  POSTGRES_DB: {{ .Values.postgres.db }}
-  POSTGRES_HOST: {{ .Values.postgres.host }}
----
-apiVersion: v1
-kind: Secret
-metadata:
-  name: tls-secret
-  namespace: {{ .Values.global.namespace }}
-type: kubernetes.io/tls
-data:
-  tls.crt: "{{ .Values.secret.tls.cert }}"
-  tls.key: "{{ .Values.secret.tls.key }}"
----
-apiVersion: v1
-kind: Secret
-metadata:
-  name: keycloak-secrets
-  namespace: {{ .Values.global.namespace }}
-type: Opaque
-stringData:
-  KEYCLOAK_ADMIN: {{ .Values.secret.KEYCLOAK_ADMIN }}
-  KEYCLOAK_ADMIN_PASSWORD: {{ .Values.secret.KEYCLOAK_ADMIN_PASSWORD }}
\ No newline at end of file
diff --git a/keycloak/src/main/helm/templates/03_storage.yml b/keycloak/src/main/helm/templates/03_storage.yml
deleted file mode 100755
index 794cbbd4a3e50149494b89a2c90fc982b7795c50..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/templates/03_storage.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-# kind: PersistentVolume
-# apiVersion: v1
-# metadata:
-#   name: postgres-pv
-#   namespace: {{ .Values.global.namespace }}
-#   labels:
-#     type: local
-#     app: postgres
-# spec:
-#   storageClassName: "nfs-client-bup"
-#   capacity:
-#     storage: 1Gi
-#   accessModes:
-#     - ReadWriteMany
-#   persistentVolumeReclaimPolicy: Retain
-#   hostPath:
-#     path: "/mnt/data"
-# ---
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
-  name: postgres-pvc
-  namespace: {{ .Values.global.namespace }}
-  labels:
-    app: postgres
-spec:
-  storageClassName: {{ .Values.persistence.storageClass }}
-  accessModes:
-    - ReadWriteMany
-  resources:
-    requests:
-      storage: 1Gi
\ No newline at end of file
diff --git a/keycloak/src/main/helm/templates/04_postgres.yml b/keycloak/src/main/helm/templates/04_postgres.yml
deleted file mode 100755
index 839a236287c7080ce1f0f13287f33242ddda827b..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/templates/04_postgres.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-{{- if (contains "postgres" .Values.postgres.host) }}
-apiVersion: v1
-kind: Service
-metadata:
-  name: postgres
-  namespace: {{ .Values.global.namespace }}
-  labels:
-    app: postgres
-spec:
-  ports:
-  - port: 5432
-    name: postgres
-  selector:
-    app: postgres
-  type: ClusterIP
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: postgres
-  namespace: {{ .Values.global.namespace }}
-spec:
-  selector:
-    matchLabels:
-      app: postgres
-  strategy:
-    type: Recreate
-  template:
-    metadata:
-      labels:
-        app: postgres
-    spec:
-      imagePullSecrets:
-          - name: {{ .Values.global.secret }}
-      containers:
-      - image: "{{ .Values.dockerRepo }}/{{ .Values.postgres.imageName }}:{{ .Values.postgres.tag }}"
-        name: postgres
-        envFrom:
-          - secretRef:
-              name: postgres-credentials
-        env:
-          - name: PGDATA
-            value: "{{ .Values.postgres.pgdata }}"
-        ports:
-        - containerPort: 5432
-          name: postgres
-        securityContext:
-          privileged: false
-        volumeMounts:
-        - name: postgres-storage
-          mountPath: /var/lib/postgresql/data
-        resources:
-            limits:
-              memory: 512Mi
-              cpu: "1"
-            requests:
-              memory: 256Mi
-              cpu: "0.2"
-      volumes:
-      - name: postgres-storage
-        persistentVolumeClaim:
-          claimName: postgres-pvc
-{{- end }}
\ No newline at end of file
diff --git a/keycloak/src/main/helm/templates/05_keycloak.yml b/keycloak/src/main/helm/templates/05_keycloak.yml
deleted file mode 100755
index c8baf864e97692cd81c601b18214942315bb0de7..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/templates/05_keycloak.yml
+++ /dev/null
@@ -1,124 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  name: "{{ .Values.name }}"
-  namespace: "{{ .Values.global.namespace }}"
-  labels:
-    app: "{{ .Values.name }}"
-spec:
-  ports:
-    - name: http
-      port: 80
-      targetPort: 8080
-    - name: https
-      port: 443
-      targetPort: 8443
-  selector:
-    app: "{{ .Values.name }}"
-  type: LoadBalancer
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: "{{ .Values.name }}"
-  namespace: "{{ .Values.global.namespace }}"
-  labels:
-    app: "{{ .Values.name }}"
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: "{{ .Values.name }}"
-  template:
-    metadata:
-      labels:
-        app: "{{ .Values.name }}"
-    spec:
-      volumes:
-        - name: keycloak-tls
-          secret:
-            secretName: secret-tls
-      imagePullSecrets:
-          - name: "{{ .Values.global.secret }}"
-      containers:
-        - name: keycloak
-          image: "{{ .Values.dockerRepo }}/{{ .Values.deployment.imageName }}:{{ .Values.deployment.tag }}"
-          args: ["start", "--optimized"]
-          # args: ["start-dev"]
-          env:
-            - name: KEYCLOAK_ADMIN
-              valueFrom:
-                secretKeyRef:
-                  key: KEYCLOAK_ADMIN
-                  name: keycloak-secrets
-            - name: KEYCLOAK_ADMIN_PASSWORD
-              valueFrom:
-                secretKeyRef:
-                  key: KEYCLOAK_ADMIN_PASSWORD
-                  name: keycloak-secrets
-            - name: KC_PROXY
-              value: "edge"
-            - name: KC_HTTP_ENABLED
-              value: "true"
-            # TODO: Find out, if the KC_HOSTNAME parameter needs to be set and to which value
-            - name: KC_HOSTNAME
-              value: "{{ .Values.host }}"
-            # TODO: Find out, if the KC_HOSTNAME_STRICT_HTTPS paramater needs to be set and to which value
-            - name: KC_HOSTNAME_STRICT_HTTPS
-              value: "false"
-            - name: KC_HOSTNAME_STRICT
-              value: "false"
-            - name: KC_HEALTH_ENABLED
-              value: "true"
-            - name: KC_METRICS_ENABLED
-              value: "true"
-            - name: KC_LOG_LEVEL
-              value: INFO
-            - name: KC_DB
-              value: postgres
-            - name: POSTGRES_HOST
-              valueFrom:
-                secretKeyRef:
-                  name: postgres-credentials
-                  key: POSTGRES_HOST
-            - name: POSTGRES_DB
-              valueFrom:
-                secretKeyRef:
-                  name: postgres-credentials
-                  key: POSTGRES_DB
-            - name: KC_DB_URL
-              value: jdbc:postgresql://${POSTGRES_HOST}/${POSTGRES_DB}
-            - name: KC_DB_USERNAME
-              valueFrom:
-                secretKeyRef:
-                  name: postgres-credentials
-                  key: POSTGRES_USER
-            - name: KC_DB_PASSWORD
-              valueFrom:
-                secretKeyRef:
-                  name: postgres-credentials
-                  key: POSTGRES_PASSWORD
-          ports:
-            - name: http
-              containerPort: 8080
-            - name: https
-              containerPort: 8443
-          readinessProbe:
-            httpGet:
-              path: /health/ready
-              port: http
-            initialDelaySeconds: 20
-            periodSeconds: 10
-          livenessProbe:
-            httpGet:
-              path: /health/live
-              port: http
-            initialDelaySeconds: 60
-            periodSeconds: 30
-          resources:
-            limits:
-              memory: 1024Mi
-              cpu: "1"
-            requests:
-              memory: 512Mi
-              cpu: "0.5"
diff --git a/keycloak/src/main/helm/templates/06_ingress.yml b/keycloak/src/main/helm/templates/06_ingress.yml
deleted file mode 100755
index 1399e2aa2e5b86d16ab6149a2209983e1a95050a..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/templates/06_ingress.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
-  name: {{ .Values.name }}
-  namespace: {{ .Values.global.namespace }}
-spec:
-  {{- if (contains "ozg-cloud.systems" .Values.host) }}
-  tls:
-  - hosts:
-    - {{ .Values.host }}
-    secretName: tls-secret
-  {{- end }}
-  rules:
-    - host: {{ .Values.host }}
-      http:
-        paths:
-          - path: /
-            pathType: Prefix
-            backend:
-              service:
-                name: {{ .Values.name }}
-                port:
-                  name: http
\ No newline at end of file
diff --git a/keycloak/src/main/helm/templates/_helpers.tpl b/keycloak/src/main/helm/templates/_helpers.tpl
deleted file mode 100755
index 24fbdd19c62752eba0310ef9e30e5d8ea8e5fa70..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/templates/_helpers.tpl
+++ /dev/null
@@ -1,62 +0,0 @@
-{{/*
-Expand the name of the chart.
-*/}}
-{{- define "ozg-keycloak.name" -}}
-{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
-{{- end }}
-
-{{/*
-Create a default fully qualified app name.
-We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
-If release name contains chart name it will be used as a full name.
-*/}}
-{{- define "ozg-keycloak.fullname" -}}
-{{- if .Values.fullnameOverride }}
-{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
-{{- else }}
-{{- $name := default .Chart.Name .Values.nameOverride }}
-{{- if contains $name .Release.Name }}
-{{- .Release.Name | trunc 63 | trimSuffix "-" }}
-{{- else }}
-{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
-{{- end }}
-{{- end }}
-{{- end }}
-
-{{/*
-Create chart name and version as used by the chart label.
-*/}}
-{{- define "ozg-keycloak.chart" -}}
-{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
-{{- end }}
-
-{{/*
-Common labels
-*/}}
-{{- define "ozg-keycloak.labels" -}}
-helm.sh/chart: {{ include "ozg-keycloak.chart" . }}
-{{ include "ozg-keycloak.selectorLabels" . }}
-{{- if .Chart.AppVersion }}
-app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
-{{- end }}
-app.kubernetes.io/managed-by: {{ .Release.Service }}
-{{- end }}
-
-{{/*
-Selector labels
-*/}}
-{{- define "ozg-keycloak.selectorLabels" -}}
-app.kubernetes.io/name: {{ include "ozg-keycloak.name" . }}
-app.kubernetes.io/instance: {{ .Release.Name }}
-{{- end }}
-
-{{/*
-Create the name of the service account to use
-*/}}
-{{- define "ozg-keycloak.serviceAccountName" -}}
-{{- if .Values.serviceAccount.create }}
-{{- default (include "ozg-keycloak.fullname" .) .Values.serviceAccount.name }}
-{{- else }}
-{{- default "default" .Values.serviceAccount.name }}
-{{- end }}
-{{- end }}
diff --git a/keycloak/src/main/helm/values.yaml b/keycloak/src/main/helm/values.yaml
deleted file mode 100755
index 609e24dee68b1224cf0f0372f044e1502ac830c6..0000000000000000000000000000000000000000
--- a/keycloak/src/main/helm/values.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-name: "${appName}"
-hostname: keycloak
-dockerRepo: "docker.ozg-sh.de"
-host: "${namespace}-{{appName}}.pidev.mgm-tp.com"
-
-deployment:
-  imageName: "com.mgmtp.bup.ozg/${appName}"
-  tag: "${chartVersion}"
-global:
-  namespace: "${namespace}"
-  secret: "${namespace}-pull-secret"
-postgres:
-    host: "postgres"
-    user: "keycloak"
-    password: "aab++112"
-    db:  "postgres"
-    imageName: "postgres"
-    tag: "16.0"
-    pgdata: "/var/lib/postgresql/data"
-persistence:
-  storageClass: "nfs-client-bup"
-secret:
-  tls:
-    crt: "SET_BY_ANSIBLE for open shift"
-    key: "SET_BY_ANSIBLE for open shift"
-  KEYCLOAK_ADMIN: admin
-  KEYCLOAK_ADMIN_PASSWORD: admin
-
-
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000000000000000000000000000000000000..b7f064624f8911a9d50912d2c3e163a1eb685209
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,287 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.1.1
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME
+      else
+        JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`\\unset -f command; \\command -v java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  printf '%s' "$(cd "$basedir"; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname $0)")
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    else
+      wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $wrapperUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        QUIET="--quiet"
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+          QUIET=""
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget $QUIET "$wrapperUrl" -O "$wrapperJarPath"
+        else
+            wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath"
+        fi
+        [ $? -eq 0 ] || rm -f "$wrapperJarPath"
+    elif command -v curl > /dev/null; then
+        QUIET="--silent"
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+          QUIET=""
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L
+        else
+            curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L
+        fi
+        [ $? -eq 0 ] || rm -f "$wrapperJarPath"
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaSource=`cygpath --path --windows "$javaSource"`
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaSource" ]; then
+            if [ ! -e "$javaClass" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaSource")
+            fi
+            if [ -e "$javaClass" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..474c9d6b74cd3201de028ccee0332a53dc993d56
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,187 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.1.1
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %WRAPPER_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/ozg-antragsraum-server/build.gradle b/ozg-antragsraum-server/build.gradle
deleted file mode 100644
index 285023799014ab7a5f672eb68348266c9f7838fc..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/build.gradle
+++ /dev/null
@@ -1,185 +0,0 @@
-buildscript {
-    ext {
-        grpcTestVersion = '1.19.0'
-        netDevhGrpcVersion = '2.15.0.RELEASE'
-        restAssuredVersion = '5.3.1'
-        springOpenApiVersion = '2.1.0'
-        commonsIoVersion = '2.13.0'
-        modulithVersion = '1.0.2'
-        grpcVersion = '1.57.2'
-        infoManagerInterfaceVersion = '0.0.2-1.a7b24d5-SNAPSHOT'
-        clamavClientVersion = '2.1.2'
-    }
-}
-
-plugins {
-    id "java"
-    id "org.springframework.boot" version '3.1.5'
-    id "io.spring.dependency-management" version '1.1.3'
-    id "com.cinnober.gradle.semver-git" version "${semver_plugin}" apply false
-    id "jacoco"
-    id "org.sonarqube" version "${sonarqube_plugin}"
-    id "org.owasp.dependencycheck" version '8.4.2'
-    id "org.unbroken-dome.helm" version "${helm_plugin}"
-    id "org.unbroken-dome.helm-publish" version "${helm_plugin}"
-    id "com.github.hierynomus.license" version "0.16.1"
-}
-
-java {
-    sourceCompatibility = '17'
-}
-
-jacocoTestReport {
-    reports {
-        xml.required = true
-    }
-}
-
-dependencyCheck {
-    formats = ['HTML', 'XML']
-}
-
-configurations {
-    compileOnly {
-        extendsFrom annotationProcessor
-    }
-}
-
-sourceSets {
-    integrationTest {
-        resources.srcDir("src/test/resources")
-    }
-}
-
-
-license {
-    header = file("src/main/resources/META-INF/LICENSE.txt")
-    excludes([
-            "**/*.txt",
-            "**/*.conf",
-            "**/*.yaml",
-            "**/*.yml",
-            "**/*.json",
-            "**/*.properties",
-            "**/*.pdf",
-            "**/*.odt",
-            "**/*.docx",
-            "**/*.zip"
-    ])
-    strictCheck = true
-    licenseFormatMain.skipExistingHeaders = true
-    // we use a slightly different header format than "javadoc_style".. therefore we need to redefine it here - or change our format.
-    headerDefinitions {
-        javadoc_style {
-            firstLine = "/*"
-            endLine = " */"
-            firstLineDetectionPattern = "(\\s|\\t)*/\\*.*\$"
-            lastLineDetectionPattern = ".*\\*/(\\s|\\t)*\$"
-            allowBlankLines = true
-            beforeEachLine = " * "
-            isMultiline = true
-        }
-    }
-
-}
-
-
-dependencies {
-    implementation 'org.springframework.boot:spring-boot-starter-web'
-    implementation 'org.springframework.modulith:spring-modulith-starter-core'
-    implementation 'org.springframework.boot:spring-boot-starter-actuator'
-    implementation 'org.springframework.boot:spring-boot-starter-validation'
-    implementation 'org.springframework.boot:spring-boot-starter-security'
-    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
-    implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
-    implementation 'org.springframework.boot:spring-boot-starter-webflux'
-    implementation "net.devh:grpc-client-spring-boot-starter:${netDevhGrpcVersion}"
-    implementation "net.devh:grpc-server-spring-boot-starter:${netDevhGrpcVersion}"
-    implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springOpenApiVersion}"
-    implementation "commons-io:commons-io:${commonsIoVersion}"
-    implementation "com.mgmtp.bup.ozg:ozg-info-manager-interface:${infoManagerInterfaceVersion}"
-    // TODO: replace by version from mgm repo when available
-    implementation fileTree(dir: 'lib', include: ['*.jar'])
-    implementation "xyz.capybara:clamav-client:${clamavClientVersion}"
-    compileOnly 'org.projectlombok:lombok'
-    developmentOnly 'org.springframework.boot:spring-boot-devtools'
-    annotationProcessor 'org.projectlombok:lombok'
-    testImplementation 'org.springframework.boot:spring-boot-starter-test'
-    testImplementation 'org.springframework.modulith:spring-modulith-starter-test'
-    testImplementation "io.grpc:grpc-testing:${grpcTestVersion}"
-    testImplementation "io.rest-assured:rest-assured:${restAssuredVersion}"
-    testImplementation "net.devh:grpc-server-spring-boot-starter:${netDevhGrpcVersion}"
-    testImplementation 'com.c4-soft.springaddons:spring-addons-oauth2-test:7.1.10'
-    testCompileOnly 'org.projectlombok:lombok'
-    testAnnotationProcessor 'org.projectlombok:lombok'
-}
-
-dependencyManagement {
-    imports {
-        mavenBom "org.springframework.modulith:spring-modulith-bom:${modulithVersion}"
-        mavenBom "io.grpc:grpc-bom:${grpcVersion}"
-    }
-}
-
-springBoot {
-    buildInfo()
-}
-
-tasks.named('test') {
-    useJUnitPlatform()
-}
-
-bootJar {
-    manifest {
-        attributes 'Start-Class': 'de.mgm.bup.ozg.antragsraum.AntragsRaumApplication'
-    }
-}
-
-plugins.withType(JacocoPlugin) {
-    tasks["test"].finalizedBy 'jacocoTestReport'
-}
-
-def isDev = 'git rev-parse --abbrev-ref HEAD'.execute().text.trim() == 'dev'
-bootBuildImage {
-    createdDate = "now"
-    builder = "${project.dockerRegistryPull}/paketobuildpacks/builder:base"
-    runImage = "${project.dockerRegistryPull}/paketobuildpacks/run:base-cnb"
-    imageName = "${project.dockerRegistryForPublish}/${project.group}/${project.name}:${project.version}"
-    tags = isDev ? ["${project.dockerRegistryForPublish}/${project.group}/${project.name}:latest"] : []
-    docker {
-        builderRegistry {
-            username = project.findProperty("repository_pull_username") ?: System.getenv('repository_pull_username')
-            password = project.findProperty("repository_pull_password") ?: System.getenv('repository_pull_password')
-            url = "${project.dockerRegistryPull}"
-        }
-        publishRegistry {
-            username = project.findProperty("repository_publish_username") ?: System.getenv('repository_publish_username')
-            password = project.findProperty("repository_publish_password") ?: System.getenv('repository_publish_password')
-            url = "${project.dockerRegistryForPublish}"
-        }
-    }
-}
-
-helm {
-    filtering {
-        values.put "namespace", findProperty("namespace")
-        values.put "appName", project.name
-    }
-    publishing {
-        repositories {
-            nexus {
-                url = uri("${helmRepositoryHost}")
-                repository = isSnapshot() ? "${helmRepositorySnapshot}" : "${helmRepositoryRelease}"
-                apiVersion = 'v1'
-                credentials {
-                    username = project.findProperty("repository_helm_username") ?: System.getenv('repository_helm_username')
-                    password = project.findProperty("repository_helm_password") ?: System.getenv('repository_helm_password')
-                }
-            }
-        }
-    }
-}
-
-boolean isSnapshot() {
-    project.version.matches(".*SNAPSHOT.*")
-}
diff --git a/ozg-antragsraum-server/lib/pluto-interface-1.16.0-SNAPSHOT-sources.jar b/ozg-antragsraum-server/lib/pluto-interface-1.16.0-SNAPSHOT-sources.jar
deleted file mode 100644
index b0137317b6aa433a1305c4c9cf2901a1d051c446..0000000000000000000000000000000000000000
Binary files a/ozg-antragsraum-server/lib/pluto-interface-1.16.0-SNAPSHOT-sources.jar and /dev/null differ
diff --git a/ozg-antragsraum-server/lib/pluto-interface-1.16.0-SNAPSHOT.jar b/ozg-antragsraum-server/lib/pluto-interface-1.16.0-SNAPSHOT.jar
deleted file mode 100644
index 64d48a88929300da72f1a83148f18c98b4a1034f..0000000000000000000000000000000000000000
Binary files a/ozg-antragsraum-server/lib/pluto-interface-1.16.0-SNAPSHOT.jar and /dev/null differ
diff --git a/ozg-antragsraum-server/src/main/docker/docker-compose.yml b/ozg-antragsraum-server/src/main/docker/docker-compose.yml
deleted file mode 100644
index fe0d8baae410f59a6cf01c10ad31649e907d275d..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/docker/docker-compose.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-version: '3'
-volumes:
-  mongodb-data:
-
-services:
-  ozg-mongodb:
-    image: mongo:4
-    ports:
-      - "27017:27017"
-    environment:
-      - MONGODB_EXTRA_FLAGS=--profile=2
-    volumes:
-      - mongodb-data:/data/db
-  ozg-info-manager:
-    image: dockerregistry.mgm-tp.com/com.mgmtp.bup.ozg/ozg-info-manager:latest
-    platform: linux/amd64
-    environment:
-      - SPRING_DATA_MONGODB_HOST=ozg-mongodb
-      - SPRING_PROFILES_ACTIVE=dev
-    ports:
-      - "9091:9090"
-      - "8084:8081"
-    depends_on:
-      - ozg-mongodb
-  ozg-clamav:
-    container_name: clamav_container
-    ports:
-      - '3310:3310'
-    image: 'docker-repos.dockerregistry.mgm-tp.com/clamav/clamav:stable'
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/main/helm/Chart.yaml b/ozg-antragsraum-server/src/main/helm/Chart.yaml
deleted file mode 100644
index 1402e335026fbe004a8fe8dad4c41092c84c1654..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/Chart.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-apiVersion: v2
-name: GETS_REPLACED_BY_HELM_PLUGIN
-description: A Helm chart for Kubernetes
-
-# A chart can be either an 'application' or a 'library' chart.
-#
-# Application charts are a collection of templates that can be packaged into versioned archives
-# to be deployed.
-#
-# Library charts provide useful utilities or functions for the chart developer. They're included as
-# a dependency of application charts to inject those utilities and functions into the rendering
-# pipeline. Library charts do not define any templates and therefore cannot be deployed.
-type: application
-
-# This is the chart version. This version number should be incremented each time you make changes
-# to the chart and its templates, including the app version.
-# Versions are expected to follow Semantic Versioning (https://semver.org/)
-#version: "${chartVersion}"
-version: "${chartVersion}"
-
-# This is the version number of the application being deployed. This version number should be
-# incremented each time you make changes to the application. Versions are not expected to
-# follow Semantic Versioning. They should reflect the version the application is using.
-# It is recommended to use it with quotes.
-appVersion: "${chartVersion}"
-
diff --git a/ozg-antragsraum-server/src/main/helm/templates/NOTES.txt b/ozg-antragsraum-server/src/main/helm/templates/NOTES.txt
deleted file mode 100644
index 25035fa21e6151396e5de085d6dbd1659e03bccb..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/templates/NOTES.txt
+++ /dev/null
@@ -1 +0,0 @@
-Check https://{{ .Values.global.namespace }}-{{ .Values.hostname }}.pidev.mgm-tp.com/actuator/info to test the deployment.
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/main/helm/templates/_helpers.tpl b/ozg-antragsraum-server/src/main/helm/templates/_helpers.tpl
deleted file mode 100644
index 405686f82887971c2b729d78da2289f1f599323d..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/templates/_helpers.tpl
+++ /dev/null
@@ -1,62 +0,0 @@
-{{/*
-Expand the name of the chart.
-*/}}
-{{- define "ozg-frontend-docs.name" -}}
-{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
-{{- end }}
-
-{{/*
-Create a default fully qualified app name.
-We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
-If release name contains chart name it will be used as a full name.
-*/}}
-{{- define "ozg-frontend-docs.fullname" -}}
-{{- if .Values.fullnameOverride }}
-{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
-{{- else }}
-{{- $name := default .Chart.Name .Values.nameOverride }}
-{{- if contains $name .Release.Name }}
-{{- .Release.Name | trunc 63 | trimSuffix "-" }}
-{{- else }}
-{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
-{{- end }}
-{{- end }}
-{{- end }}
-
-{{/*
-Create chart name and version as used by the chart label.
-*/}}
-{{- define "ozg-frontend-docs.chart" -}}
-{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
-{{- end }}
-
-{{/*
-Common labels
-*/}}
-{{- define "ozg-frontend-docs.labels" -}}
-helm.sh/chart: {{ include "ozg-frontend-docs.chart" . }}
-{{ include "ozg-frontend-docs.selectorLabels" . }}
-{{- if .Chart.AppVersion }}
-app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
-{{- end }}
-app.kubernetes.io/managed-by: {{ .Release.Service }}
-{{- end }}
-
-{{/*
-Selector labels
-*/}}
-{{- define "ozg-frontend-docs.selectorLabels" -}}
-app.kubernetes.io/name: {{ include "ozg-frontend-docs.name" . }}
-app.kubernetes.io/instance: {{ .Release.Name }}
-{{- end }}
-
-{{/*
-Create the name of the service account to use
-*/}}
-{{- define "ozg-frontend-docs.serviceAccountName" -}}
-{{- if .Values.serviceAccount.create }}
-{{- default (include "ozg-frontend-docs.fullname" .) .Values.serviceAccount.name }}
-{{- else }}
-{{- default "default" .Values.serviceAccount.name }}
-{{- end }}
-{{- end }}
diff --git a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-configmap.yaml b/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-configmap.yaml
deleted file mode 100644
index 95aa9717b6240f6929fd921f81e858e71059c30e..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-configmap.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: "{{ .Values.name }}-configmap"
-data:
-  keycloak.clientId: "{{ .Values.keycloak.clientId }}"
-  keycloak.clientSecret: "{{ .Values.keycloak.clientSecret }}"
-  keycloak.authorizationGrantType: "{{ .Values.keycloak.authorizationGrantType }}"
-  keycloak.scope: "{{ .Values.keycloak.scope }}"
-  keycloak.issuerUri: "{{ .Values.keycloak.issuerUri }}"
-  jwt.issuerUri: "{{ .Values.jwt.issuerUri }}"
-  grpcclient.infomanager.address: "{{ .Values.grpcclient.infomanager.address }}"
-  grpcclient.infomanager.negotiationtype:  "{{ .Values.grpcclient.infomanager.negotiationtype }}"
-  grpc.ozgservice.stubs.enabled: "{{ .Values.grpc.ozgservice.stubs.enabled }}"
-  swagger.oauth.authorizationurl: "{{ .Values.swagger.oauth.authorizationurl }}"
-  swagger.oauth.tokenurl: "{{ .Values.swagger.oauth.tokenurl }}"
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-deployment.yaml b/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-deployment.yaml
deleted file mode 100644
index 3197fe5da678cab903a65c1eca10d8e172e7509a..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-deployment.yaml
+++ /dev/null
@@ -1,132 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: "{{ .Values.name }}-deployment"
-  labels:
-    app: {{ .Values.name }}
-  namespace: {{ .Values.global.namespace }}
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: {{ .Values.name }}
-  template:
-    metadata:
-      labels:
-        app: {{ .Values.name }}
-    spec:
-      imagePullSecrets:
-        - name: {{ .Values.global.secret }}
-      containers:
-        - name: {{ .Values.name }}
-          image: "{{ .Values.deployment.image }}:{{ .Values.deployment.tag }}"
-          ports:
-            - containerPort: 8080
-            - containerPort: 8081
-          volumeMounts:
-            - mountPath: /tmp/antragsraum
-              name: tmp-volume
-          env:
-            - name: GRPC_CLIENT_INFO_MANAGER_ADDRESS
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: grpcclient.infomanager.address
-            - name: GRPC_CLIENT_INFO_MANAGER_NEGOTIATIONTYPE
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: grpcclient.infomanager.negotiationtype
-            - name: OZGCLOUD_GRPC_CLIENT_NEGOTIATIONTYPE
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: grpcclient.infomanager.negotiationtype
-            - name: SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_CLIENTID
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: keycloak.clientId
-            - name: SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_CLIENTSECRET
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: keycloak.clientSecret
-            - name: SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_AUTHORIZATIONGRANTTYPE
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: keycloak.authorizationGrantType
-            - name: SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_SCOPE
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: keycloak.scope
-            - name: SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KEYCLOAK_ISSUERURI
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: keycloak.issuerUri
-            - name: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUERURI
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: jwt.issuerUri
-            - name: GRPC_OZG_SERVICE_STUBS_ENABLED
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: grpc.ozgservice.stubs.enabled
-            - name: springdoc.swagger-ui.oauth.client-id
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: keycloak.clientId
-            - name: springdoc.swagger-ui.oauth.client-secret
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: keycloak.clientSecret
-            - name: springdoc.oAuthFlow.authorizationUrl
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: swagger.oauth.authorizationurl
-            - name: springdoc.oAuthFlow.tokenUrl
-              valueFrom:
-                configMapKeyRef:
-                  name: "{{ .Values.name }}-configmap"
-                  key: swagger.oauth.tokenurl
-          resources:
-            requests:
-              memory: "1Gi"
-              cpu: "100m"
-            limits:
-              memory: "2Gi"
-              cpu: "500m"
-          readinessProbe:
-            httpGet:
-              path: /actuator/health
-              port: 8081
-            initialDelaySeconds: 180
-            periodSeconds: 30
-          livenessProbe:
-            httpGet:
-              path: /actuator/health
-              port: 8081
-            initialDelaySeconds: 180
-            periodSeconds: 30
-        - name: clamav
-          image: "{{ .Values.clamav.image }}:{{ .Values.clamav.tag }}"
-          ports:
-            - containerPort: 3310
-          resources:
-            requests:
-              memory: "1Gi"
-              cpu: "100m"
-            limits:
-              memory: "2Gi"
-              cpu: "500m"
-      volumes:
-        - name: tmp-volume
-          emptyDir: {}
diff --git a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-ingress.yaml b/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-ingress.yaml
deleted file mode 100644
index 3ed25c38bc4c2e78d1d7981ad2fcfe378daab5b9..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-ingress.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
-  name: "{{ .Values.name }}-ingress"
-  namespace: {{ .Values.global.namespace }}
-spec:
-  rules:
-    - http:
-        paths:
-          - path: /
-            pathType: Prefix
-            backend:
-              service:
-                name: "{{ .Values.name }}-service"
-                port:
-                  number: 8080
-          - path: /actuator
-            pathType: Prefix
-            backend:
-              service:
-                name: "{{ .Values.name }}-service"
-                port:
-                  number: 8081
-      host: "{{ .Values.host }}"
-  # tls:
-  #   - hosts:
-  #     - hello.pidev.mgm-tp.com
-  #     secretName: hello-tls-secret
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-service.yaml b/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-service.yaml
deleted file mode 100644
index af0cb60bc21713238f808c5910cb621b34a6dac4..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/templates/ozg-antragsraum-service.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  name: "{{ .Values.name }}-service"
-  namespace: {{ .Values.global.namespace }}
-spec:
-  selector:
-    app: {{ .Values.name }}
-  ports:
-    - name: http
-      protocol: TCP
-      port: 8080
-      targetPort: 8080
-    - name: management
-      protocol: TCP
-      port: 8081
-      targetPort: 8081
diff --git a/ozg-antragsraum-server/src/main/helm/values.yaml b/ozg-antragsraum-server/src/main/helm/values.yaml
deleted file mode 100644
index b592ecf240837f92849d35e90666808744602d41..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/helm/values.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: "${appName}"
-hostname: antragsraum
-host: "${namespace}-antragsraum.pidev.mgm-tp.com"
-deployment:
-  image: "docker.ozg-sh.de/com.mgmtp.bup.ozg/${appName}"
-  tag: "${chartVersion}"
-global:
-  namespace: "${namespace}"
-  secret: "${namespace}-pull-secret"
-keycloak:
-  clientId: abc
-  clientSecret: s3cr3t
-  authorizationGrantType: def
-  scope: ghi
-  issuerUri: jkl
-jwt:
-  issuerUri: mno
-grpcclient:
-  infomanager:
-    address: "static://ozg-info-manager-service:9090"
-    negotiationtype: PLAINTEXT
-clamav:
-  image: docker.ozg-sh.de/clamav/clamav
-  tag: "stable"
-grpc:
-  ozgservice:
-    stubs:
-      enabled: "true"
-swagger:
-  oauth:
-    authorizationurl: "https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci/protocol/openid-connect/auth"
-    tokenurl: "https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci/protocol/openid-connect/token"
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplication.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplication.java
deleted file mode 100644
index 4ee12b96176c6cdbb5d69f63e8e6304efe7ab97e..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplication.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum;
-
-import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.Bean;
-import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
-import org.springframework.security.concurrent.DelegatingSecurityContextRunnable;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-
-@SpringBootApplication
-@EnableWebMvc
-@EnableAsync
-public class AntragsRaumApplication {
-
-    public static void main(String[] args) {
-        SpringApplication.run(AntragsRaumApplication.class, args);
-    }
-
-    @Bean
-    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
-        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
-
-        executor.setThreadNamePrefix("async-");
-        executor.setTaskDecorator(DelegatingSecurityContextRunnable::new);
-        return executor;
-    }
-
-    @Bean
-    public CallScope callScope() {
-        return new CallScope();
-    }
-
-    @Bean
-    public static BeanFactoryPostProcessor beanFactoryPostProcessor(CallScope callScope) {
-        return new CallBeanFactoryPostProcessor(callScope);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/SecurityConfiguration.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/SecurityConfiguration.java
deleted file mode 100644
index db0a6cd6d3a5a4a8c47ac89df4d867e8a32ccfdd..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/SecurityConfiguration.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.web.SecurityFilterChain;
-import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
-import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
-
-import static org.springframework.security.config.Customizer.*;
-
-@Configuration
-@EnableWebSecurity
-public class SecurityConfiguration {
-    @Bean
-    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
-        return new NullAuthenticatedSessionStrategy();
-    }
-
-    @Bean
-    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
-        http.authorizeHttpRequests(authorize -> authorize
-                .requestMatchers("/", "/actuator/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
-                .requestMatchers("/api/**").authenticated()
-                .anyRequest().denyAll()
-        ).oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults()));
-        return http.build();
-    }
-
-    @Bean
-    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
-        return http.getSharedObject(AuthenticationManagerBuilder.class)
-                .build();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/ChunkedFileSender.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/ChunkedFileSender.java
deleted file mode 100644
index ba422bcbf5ab9dad2f8e6c7ebd54bd9fb8cbef50..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/ChunkedFileSender.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import io.grpc.stub.CallStreamObserver;
-import lombok.AllArgsConstructor;
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Function;
-
-@AllArgsConstructor
-class ChunkedFileSender<T> {
-    private final AtomicBoolean hasUploadFile = new AtomicBoolean(true);
-    private final InputStream uploadStream;
-    private final int chunkSize;
-    private final Function<byte[], T> buildChunkRequest;
-    private T requestMetadata;
-
-    public void sendChunkTo(CallStreamObserver<T> streamObserver) {
-        if (hasUploadFile.get()) {
-            sendMetadata(streamObserver);
-            int size = sendNextChunk(streamObserver);
-            if (size < chunkSize) {
-                handleFileEndReached(streamObserver);
-            }
-        }
-    }
-
-    private void sendMetadata(CallStreamObserver<T> streamObserver) {
-        if (requestMetadata != null) {
-            streamObserver.onNext(requestMetadata);
-            requestMetadata = null;
-        }
-    }
-
-    private int sendNextChunk(CallStreamObserver<T> streamObserver) {
-        byte[] content = readFromStream();
-        var size = content.length;
-        if (size > 0) {
-            streamObserver.onNext(buildChunkRequest.apply(content));
-        }
-        return size;
-    }
-
-    private byte[] readFromStream() {
-        try {
-            return uploadStream.readNBytes(chunkSize);
-        } catch (IOException e) {
-            throw new RuntimeException("Error on sending a single chunk", e);
-        }
-    }
-
-    private void handleFileEndReached(CallStreamObserver<T> streamObserver) {
-        IOUtils.closeQuietly(uploadStream);
-        streamObserver.onCompleted();
-        hasUploadFile.getAndSet(false);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileController.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileController.java
deleted file mode 100644
index 15e554b41ca4fdea40e1212faa2a8daba2a0b3f9..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileController.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEventService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.headers.Header;
-import io.swagger.v3.oas.annotations.media.Content;
-import io.swagger.v3.oas.annotations.media.ExampleObject;
-import io.swagger.v3.oas.annotations.responses.ApiResponse;
-import io.swagger.v3.oas.annotations.responses.ApiResponses;
-import io.swagger.v3.oas.annotations.security.SecurityRequirement;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
-
-import java.net.URI;
-import java.util.concurrent.CompletableFuture;
-
-@RestController
-@RequestMapping(FileController.PATH)
-@RequiredArgsConstructor
-@SecurityRequirement(name = "security_auth")
-public class FileController {
-    public static final String PATH = "/api/file/";
-    static final String PATH_PATTERN = PATH + "%s/%s";
-    private final @NonNull FileService fileService;
-    private final @NonNull NachrichtEventService nachrichtEventService;
-
-    @Operation(summary = "Download file content")
-    @GetMapping(value = "{rueckfrageId}/{fileId}", produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE,
-            "images/*"})
-    public ResponseEntity<StreamingResponseBody> getFileData(
-            @Parameter(description = "The id of the clerk message", example = "6358fd4146811d04010f44d0") @PathVariable String rueckfrageId,
-            @Parameter(description = "The id of the file", example = "6358fd0bee7a051389cdd788") @PathVariable String fileId) {
-        var ozgFile = getFile(fileId, rueckfrageId);
-
-        return buildResponseEntity(ozgFile, createDownloadStreamingBody(fileId, rueckfrageId));
-    }
-
-    public OzgFile getFile(String fileId, String rueckfrageId) {
-        var address = nachrichtEventService.getServiceUrlOfNachricht(rueckfrageId);
-        return fileService.getFile(fileId, address);
-    }
-
-    private ResponseEntity<StreamingResponseBody> buildResponseEntity(OzgFile ozgFile, StreamingResponseBody responseBody) {
-        return ResponseEntity.ok()
-                .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", ozgFile.fileName()))
-                .contentLength(ozgFile.fileSize())
-                .contentType(MediaType.valueOf(ozgFile.contentType()))
-                .body(responseBody);
-    }
-
-    private StreamingResponseBody createDownloadStreamingBody(String fileId, String rueckfrageId) {
-        var address = nachrichtEventService.getServiceUrlOfNachricht(rueckfrageId);
-        return out -> fileService.downloadFileContent(fileId, out, address);
-    }
-
-    @Operation(summary = "Upload file content",
-            description = "You can use cURL to upload a file example: curl -F file=@TestDokument.pdf http://localhost:8080/api/file/[vorgangid]/[rueckfrageid] -v")
-    @ApiResponses(value = {
-            @ApiResponse(responseCode = "201", description = "The file has been created",
-                    content = @Content(examples = @ExampleObject(value = "6358fd0bee7a051389cdd788", description = "The id of the file created")),
-                    headers = {@Header(name = "Location", description = "The path of of the uploaded file with its id")})
-    })
-    @PostMapping(value = "{vorgangId}/{rueckfrageId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-    @Async
-    public CompletableFuture<ResponseEntity<String>> uploadFile(
-            @Parameter(description = "The id of the Vorgang the clerk message belongs to", example = "6358fd0bee7a051389cdd787") @PathVariable @NonNull String vorgangId,
-            @Parameter(description = "The id of the clerk message the user responds to", example = "6358fd4146811d04010f44d0") @PathVariable @NonNull String rueckfrageId,
-            @RequestBody MultipartFile file) {
-        var address = nachrichtEventService.getServiceUrlOfNachricht(rueckfrageId);
-        var fileIdFuture = fileService.upload(vorgangId, address, file);
-
-        return fileIdFuture.thenApply(fileId -> ResponseEntity.
-                created(URI.create(PATH_PATTERN.formatted(vorgangId, fileId))).body(fileId));
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcClient.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcClient.java
deleted file mode 100644
index 1dcd94bb7687da9c39d70fd085a1a08f9cec0d51..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcClient.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import com.google.protobuf.ByteString;
-import de.itvsh.ozg.pluto.grpc.binaryFile.*;
-import de.mgm.bup.ozg.antragsraum.callcontext.ContextService;
-import de.mgm.bup.ozg.antragsraum.common.ChannelPropertiesFactory;
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import io.grpc.stub.StreamObserver;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
-import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
-import org.springframework.stereotype.Component;
-
-import java.io.OutputStream;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@Component
-@RequiredArgsConstructor
-class FileGrpcClient {
-    public static final int CHUNK_SIZE = 255 * 1024;
-
-    private final @NonNull GlobalClientInterceptorRegistry registry;
-    private final @NonNull ChannelPropertiesFactory channelPropertiesFactory;
-    private final @NonNull ContextService contextService;
-
-    GrpcChannelFactory getChannelFactory(String channelAddress) {
-        return de.mgm.bup.ozg.antragsraum.common.GrpcChannelFactory.getChannelFactory(channelPropertiesFactory.getGrpcChannelsProperties(channelAddress), registry);
-    }
-
-    GrpcFindFilesResponse findBinaryFilesMetaData(GrpcBinaryFilesRequest request, String address) {
-        try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
-            var channel = channelFactory.createChannel(address);
-            var serviceStub = BinaryFileServiceGrpc.newBlockingStub(channel);
-
-            return getBinaryFilesMetaData(request, serviceStub);
-        }
-    }
-
-    GrpcFindFilesResponse getBinaryFilesMetaData(GrpcBinaryFilesRequest request, BinaryFileServiceGrpc.BinaryFileServiceBlockingStub fileServiceBlockingStub) {
-        return fileServiceBlockingStub.findBinaryFilesMetaData(request);
-    }
-
-    StreamObserver<GrpcUploadBinaryFileRequest> uploadFile(UploadOzgFile uploadFile, String address, CompletableFuture<String> fileIdFuture) {
-        try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
-            var channel = channelFactory.createChannel(address);
-            var responseObserver = createUploadFileObserver(fileIdFuture, uploadFile);
-            var serviceStub = BinaryFileServiceGrpc.newStub(channel);
-
-            return doUploadFiles(responseObserver, serviceStub);
-        }
-    }
-
-    StreamObserver<GrpcUploadBinaryFileRequest> doUploadFiles(FileUploadStreamObserver observer, BinaryFileServiceGrpc.BinaryFileServiceStub asyncServiceStub) {
-        return asyncServiceStub.uploadBinaryFileAsStream(observer);
-    }
-
-    FileUploadStreamObserver createUploadFileObserver(CompletableFuture<String> fileIdFuture, UploadOzgFile uploadFile) {
-        var metadataRequest = buildMetaDataRequest(uploadFile);
-        var streamer = new ChunkedFileSender<>(uploadFile.fileContent(), CHUNK_SIZE, this::buildChunkRequest, metadataRequest);
-        return new FileUploadStreamObserver(fileIdFuture, streamer);
-    }
-
-    private GrpcUploadBinaryFileRequest buildMetaDataRequest(UploadOzgFile uploadFile) {
-        return GrpcUploadBinaryFileRequest.newBuilder()
-                .setMetadata(GrpcUploadBinaryFileMetaData.newBuilder()
-                        .setContext(contextService.createCallContext())
-                        .setVorgangId(uploadFile.vorgangId())
-                        .setContentType(uploadFile.fileData().contentType())
-                        .setSize(uploadFile.fileData().fileSize())
-                        .setFileName(uploadFile.fileData().fileName()))
-                .build();
-    }
-
-    GrpcUploadBinaryFileRequest buildChunkRequest(byte[] bytes) {
-        return GrpcUploadBinaryFileRequest.newBuilder().setFileContent((ByteString.copyFrom(bytes))).build();
-    }
-
-    void downloadFileContent(String fileId, OutputStream out, String address) {
-        try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
-            var channel = channelFactory.createChannel(address);
-            var serviceStub = BinaryFileServiceGrpc.newStub(channel);
-
-            doDownloadFileContent(fileId, out, serviceStub);
-        }
-    }
-
-    void doDownloadFileContent(String fileId, OutputStream out, BinaryFileServiceGrpc.BinaryFileServiceStub binaryFileServiceStub) {
-        var streamFuture = new CompletableFuture<Boolean>();
-        var responseObserver = createDownloadFileObserver(streamFuture, out);
-        binaryFileServiceStub.getBinaryFileContent(buildGrpcGetBinaryFileDataRequest(fileId), responseObserver);
-
-        waitUntilFutureToComplete(streamFuture);
-
-    }
-
-    FileDownloadStreamObserver createDownloadFileObserver(CompletableFuture<Boolean> streamFuture, OutputStream out) {
-        return new FileDownloadStreamObserver(streamFuture, out);
-    }
-
-    private GrpcGetBinaryFileDataRequest buildGrpcGetBinaryFileDataRequest(String fileId) {
-        return GrpcGetBinaryFileDataRequest.newBuilder().setFileId(fileId).build();
-    }
-
-    void waitUntilFutureToComplete(CompletableFuture<Boolean> streamFuture) {
-        try {
-            streamFuture.get(10, TimeUnit.MINUTES);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new TechnicalException(e);
-        } catch (ExecutionException | TimeoutException e) {
-            throw new TechnicalException(e);
-        }
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileMaxSizeConstraint.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileMaxSizeConstraint.java
deleted file mode 100644
index 40945616fe6a84b827ab1796e204191a59f3350b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileMaxSizeConstraint.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import jakarta.validation.Constraint;
-import jakarta.validation.Payload;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-
-@Constraint(validatedBy = {UploadFileSizeValidator.class})
-@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE})
-@Retention(RUNTIME)
-@Documented
-public @interface FileMaxSizeConstraint {
-    String message() default "File fileSize to large";
-
-    Class<?>[] groups() default {};
-
-    Class<? extends Payload>[] payload() default {};
-
-    long max() default UploadFileSizeValidator.DEFAULT_MAX_SIZE;
-
-    String unit() default UploadFileSizeValidator.DEFAULT_UNIT_STRING;
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteService.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteService.java
deleted file mode 100644
index 3569958e920c32fd6ab2632054e4a2b6092c055b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteService.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFilesRequest;
-import jakarta.validation.Valid;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.stereotype.Service;
-
-import java.io.OutputStream;
-import java.util.concurrent.CompletableFuture;
-
-@Log4j2
-@Service
-@RequiredArgsConstructor
-public class FileRemoteService {
-    private final @NonNull FileGrpcClient grpcClient;
-
-    OzgFile getFile(String fileId, String address) {
-        var grpcFile = grpcClient.findBinaryFilesMetaData(buildGrpcBinaryFileRequest(fileId), address).getFile(0);
-
-        return OzgFileMapper.fromGrpcFile(grpcFile);
-    }
-
-    private GrpcBinaryFilesRequest buildGrpcBinaryFileRequest(String fileId) {
-        return GrpcBinaryFilesRequest.newBuilder()
-                .addFileId(fileId)
-                .build();
-    }
-
-    CompletableFuture<String> uploadFile(@Valid UploadOzgFile uploadFile, String address) {
-        var fileIdFuture = new CompletableFuture<String>();
-        var request = grpcClient.uploadFile(uploadFile, address, fileIdFuture);
-        LOG.debug("Grpc File Upload Request: " + request);
-
-        return fileIdFuture;
-    }
-
-
-    public void downloadFileContent(String fileId, OutputStream out, String address) {
-        grpcClient.downloadFileContent(fileId, out, address);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileService.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileService.java
deleted file mode 100644
index ba850e2f3ebac92e0ae2f85f9c1f027e4f42d2f7..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileService.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import de.mgm.bup.ozg.antragsraum.common.VirusFoundException;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-
-@Log4j2
-@Service
-@RequiredArgsConstructor
-class FileService {
-    private final @NonNull FileRemoteService fileRemoteService;
-
-    private final @Autowired VirusScannerClient scanner = new VirusScannerClient();
-
-    CompletableFuture<String> upload(String vorgangId, String address, MultipartFile file) {
-        return fileRemoteService.uploadFile(createUploadFile(vorgangId, file), address);
-    }
-
-    UploadOzgFile createUploadFile(String vorgangId, MultipartFile file) throws VirusFoundException {
-        if (isClean(file)) {
-            return UploadOzgFile.builder()
-                    .fileData(OzgFile.builder()
-                            .fileName(file.getOriginalFilename())
-                            .contentType(file.getContentType())
-                            .fileSize(file.getSize())
-                            .build())
-                    .vorgangId(vorgangId)
-                    .fileContent(getInputStream(file))
-                    .build();
-        } else {
-            throw new VirusFoundException(vorgangId);
-        }
-    }
-
-    private InputStream getInputStream(MultipartFile file) {
-        try {
-            return file.getInputStream();
-        } catch (IOException e) {
-            throw new RuntimeException("Error reading File data as stream.", e);
-        }
-    }
-
-    boolean isClean(MultipartFile file) {
-        byte[] fileData;
-        try {
-            fileData = file.getBytes();
-        } catch (IOException e) {
-            throw new TechnicalException(e);
-        }
-
-        Map<String, Collection<String>> viruses = scanner.scan(fileData);
-        return viruses.isEmpty();
-    }
-
-    OzgFile getFile(String fileId, String address) {
-        return fileRemoteService.getFile(fileId, address);
-    }
-
-    void downloadFileContent(String fileId, OutputStream out, String address) {
-        fileRemoteService.downloadFileContent(fileId, out, address);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeConstraint.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeConstraint.java
deleted file mode 100644
index cdbd84f10be290db8cf36a6c355a69ddd73b5cd3..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeConstraint.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import jakarta.validation.Constraint;
-import jakarta.validation.Payload;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-@Constraint(validatedBy = {FileTypeValidator.class})
-@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE})
-@Retention(RUNTIME)
-@Documented
-public @interface FileTypeConstraint {
-    String message() default "File type not allowed";
-
-    Class<?>[] groups() default {};
-
-    Class<? extends Payload>[] payload() default {};
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/UploadFileSizeValidator.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/UploadFileSizeValidator.java
deleted file mode 100644
index 41781a216136dffeb8c486b009e4fe524fed56d9..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/UploadFileSizeValidator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import jakarta.validation.ConstraintValidator;
-import jakarta.validation.ConstraintValidatorContext;
-import lombok.RequiredArgsConstructor;
-import org.springframework.util.unit.DataSize;
-
-import java.util.Optional;
-
-@RequiredArgsConstructor
-class UploadFileSizeValidator implements ConstraintValidator<FileMaxSizeConstraint, UploadOzgFile> {
-    static final String DEFAULT_UNIT_STRING = "MB";
-    static final long DEFAULT_MAX_SIZE = 100;
-    private final FileProperties fileProperties;
-
-    @Override
-    public boolean isValid(UploadOzgFile file, ConstraintValidatorContext constraintValidatorContext) {
-        var maxFileSize = Optional.ofNullable(fileProperties.getMaxFileSize());
-
-        return maxFileSize.flatMap(
-                        dataSize -> {
-                            var val = dataSize.toBytes() > DataSize.ofBytes(file.fileData().fileSize()).toBytes();
-                            return Optional.of(val);
-                        })
-                .orElse(DataSize.ofMegabytes(DEFAULT_MAX_SIZE).toBytes() > DataSize.ofBytes(file.fileData().fileSize()).toBytes());
-
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/VirusScannerClient.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/VirusScannerClient.java
deleted file mode 100755
index 665bded58b521e6a1451686b3c5cf92f34ba465b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/VirusScannerClient.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import jakarta.annotation.PostConstruct;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import xyz.capybara.clamav.ClamavClient;
-import xyz.capybara.clamav.ClamavException;
-import xyz.capybara.clamav.commands.scan.result.ScanResult;
-
-@Service
-public class VirusScannerClient {
-
-    @Value("${clamav.server.host}")
-    private String clamAVHost;
-
-    @Value("${clamav.server.port}")
-    private int clamAVPort;
-
-    private ClamavClient clamavClient;
-
-    @PostConstruct
-    private void clamAVClientInit(){
-        this.clamavClient = new ClamavClient(clamAVHost, clamAVPort);
-    }
-
-    public Map<String, Collection<String>> scan(byte[] fileData){
-        Map<String, Collection<String>> viruses = new HashMap<>();
-
-        if(fileData != null){
-            try{
-                InputStream inputStream = new ByteArrayInputStream(fileData);
-                ScanResult scanResult = this.clamavClient.scan(inputStream);
-
-                if (scanResult instanceof ScanResult.OK) {
-                    return viruses;
-                } else if (scanResult instanceof ScanResult.VirusFound virusFound) {
-                    viruses = virusFound.getFoundViruses();
-                }
-            } catch (ClamavException e){
-                throw new TechnicalException(e);
-            }
-        }
-
-        return viruses;
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/CallContextAttachingInterceptor.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/CallContextAttachingInterceptor.java
deleted file mode 100644
index 3fd307ba9ca4104e74ee7a15eab09b88bc1adfe3..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/CallContextAttachingInterceptor.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.callcontext;
-
-import io.grpc.*;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import net.devh.boot.grpc.client.interceptor.GrpcGlobalClientInterceptor;
-
-import java.util.Objects;
-
-@Log4j2
-@GrpcGlobalClientInterceptor
-@RequiredArgsConstructor
-public class CallContextAttachingInterceptor implements ClientInterceptor {
-    private final ContextService contextService;
-
-    @Override
-    public <A, B> ClientCall<A, B> interceptCall(MethodDescriptor<A, B> method, CallOptions callOptions, Channel next) {
-        LOG.debug("Method: {}", Objects.nonNull(method) ? method.getFullMethodName() : "unknown");
-        return new CallContextAttachingClientCall<>(next.newCall(method, callOptions));
-    }
-
-    final class CallContextAttachingClientCall<A, B> extends ForwardingClientCall.SimpleForwardingClientCall<A, B> {
-
-        CallContextAttachingClientCall(ClientCall<A, B> delegate) {
-            super(delegate);
-        }
-
-        @Override
-        public void start(Listener<B> responseListener, Metadata headers) {
-            headers.merge(contextService.buildCallContextMetadata());
-            LOG.debug("Set headers to {}", headers);
-            super.start(responseListener, headers);
-        }
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteService.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteService.java
deleted file mode 100644
index 4b16fe48bbea5036c93705f2c0ad92cd3b9e97ff..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteService.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.events;
-
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcInformationRequest;
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcNachricht;
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcServiceUrlRequest;
-import de.mgm.bup.ozg.antragsraum.infomanager.InformationServiceGrpc;
-import io.grpc.StatusRuntimeException;
-import lombok.extern.log4j.Log4j2;
-import net.devh.boot.grpc.client.inject.GrpcClient;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-import java.util.Optional;
-
-@Service
-@Log4j2
-class NachrichtEventRemoteService {
-
-    @GrpcClient("info_manager")
-    InformationServiceGrpc.InformationServiceBlockingStub informationServiceBlockingClient;
-
-    List<NachrichtEvent> getNachrichtEventsOfPostfach(String postfachId) {
-        return loadEvents(postfachId).stream().map(NachrichtEventMapper::fromGrpc).toList();
-    }
-
-    private List<GrpcNachricht> loadEvents(String postfachId) {
-        var request = GrpcInformationRequest.newBuilder().setPostfachId(postfachId).build();
-
-        return informationServiceBlockingClient.getInformation(request).getNachrichtenList();
-    }
-
-    Optional<String> getServiceUrl(String nachrichtId) {
-        try {
-            var request = GrpcServiceUrlRequest.newBuilder().setNachrichtId(nachrichtId).build();
-            var response = informationServiceBlockingClient.getServiceUrlOfNachricht(request);
-            if (response.hasUrl()) {
-                return Optional.of(response.getUrl());
-            }
-        } catch (StatusRuntimeException e) {
-            LOG.error("Error loading address of OZG-Cloud instance linked to nachricht id " + nachrichtId, e);
-        }
-
-        return Optional.empty();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/BinaryFileGrpcServiceStub.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/BinaryFileGrpcServiceStub.java
deleted file mode 100644
index 45e99d5940098ba656924a2dd8b2ec3bcb6eecbc..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/BinaryFileGrpcServiceStub.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import com.google.protobuf.ByteString;
-import de.itvsh.ozg.pluto.grpc.binaryFile.*;
-import io.grpc.stub.StreamObserver;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import net.devh.boot.grpc.server.service.GrpcService;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-
-import java.io.ByteArrayInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-@Log4j2
-@GrpcService
-@ConditionalOnProperty(prefix = "grpc", name = "ozg_service.stubs.enabled")
-@RequiredArgsConstructor
-public class BinaryFileGrpcServiceStub extends BinaryFileServiceGrpc.BinaryFileServiceImplBase {
-    public static final int CHUNK_SIZE = 255;
-    private final StubDataRepository stubDataRepository;
-
-    @Override
-    public StreamObserver<GrpcUploadBinaryFileRequest> uploadBinaryFileAsStream(StreamObserver<GrpcUploadBinaryFileResponse> responseObserver) {
-        return new UploadStreamObserverStub(responseObserver, stubDataRepository);
-    }
-
-    @Override
-    public void getBinaryFileContent(GrpcGetBinaryFileDataRequest request, StreamObserver<GrpcGetBinaryFileDataResponse> responseObserver) {
-        var contentOpt = stubDataRepository.getFileContent(request.getFileId());
-
-        contentOpt.ifPresentOrElse(content -> {
-            try (ByteArrayInputStream buffer = new ByteArrayInputStream(content)) {
-                byte[] chunk = new byte[CHUNK_SIZE];
-                int read;
-                while ((read = buffer.read(chunk)) > 0) {
-                    LOG.info("Send chunk. Length: {}", read);
-                    responseObserver.onNext(GrpcGetBinaryFileDataResponse.newBuilder()
-                            .setFileContent(ByteString.copyFrom(chunk)).build());
-                }
-            } catch (IOException e) {
-                responseObserver.onError(e);
-            }
-        }, () -> {
-            try (ByteArrayInputStream bais = new ByteArrayInputStream(GrpcDataCreator.FILE_CONTENT)) {
-                responseObserver.onNext(GrpcGetBinaryFileDataResponse.newBuilder()
-                        .setFileContent(ByteString.copyFrom(bais.readAllBytes())).build());
-            } catch (IOException e) {
-                responseObserver.onError(e);
-            }
-        });
-
-        responseObserver.onCompleted();
-    }
-
-    @Override
-    public void findBinaryFilesMetaData(GrpcBinaryFilesRequest request, StreamObserver<GrpcFindFilesResponse> responseObserver) {
-        final String fileId = request.getFileId(0);
-        var fileOpt = stubDataRepository.getFileMetaData(fileId);
-
-        fileOpt.ifPresentOrElse(file -> {
-            responseObserver.onNext(
-                    GrpcFindFilesResponse.newBuilder().addFile(DataMapper.fromGrpcUploadBinaryFileMetaData(file, fileId)).build());
-            responseObserver.onCompleted();
-        }, () -> {
-            LOG.error("File with id {} not found.", fileId);
-            responseObserver.onError(new FileNotFoundException("File " + fileId + " not found in db"));
-        });
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/DataMapper.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/DataMapper.java
deleted file mode 100644
index bff0360d5680d370e2509e9c37b5d0065c8d5873..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/DataMapper.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrage;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
-import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile;
-import de.mgm.bup.ozg.antragsraum.attachments.OzgFile;
-import de.mgm.bup.ozg.antragsraum.nachricht.Nachricht;
-import de.mgm.bup.ozg.antragsraum.nachricht.ReplyNachricht;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-@Log4j2
-public class DataMapper {
-    static Nachricht fromGrpcRueckfrage(GrpcRueckfrage grpcRueckfrage) {
-        var builder = Nachricht.builder()
-                .vorgangId(grpcRueckfrage.getVorgangId())
-                .id(grpcRueckfrage.getId())
-                .topicTitle(grpcRueckfrage.getVorgangName())
-                .message(grpcRueckfrage.getText())
-                .date(new Date().getTime())
-                .attachments(getAttachments(grpcRueckfrage))
-                .replyNachrichten(getReplys(grpcRueckfrage));
-
-        return builder.build();
-    }
-
-    static List<OzgFile> getAttachments(GrpcRueckfrage grpcRueckfrage) {
-        var ozgFiles = new ArrayList<OzgFile>();
-        grpcRueckfrage.getAttachmentList().forEach(file ->
-                ozgFiles.add(
-                        OzgFile.builder()
-                                .id(file.getId())
-                                .fileName(file.getName())
-                                .fileSize(file.getSize())
-                                .contentType(file.getContentType()).build())
-        );
-        return ozgFiles;
-    }
-
-    static List<ReplyNachricht> getReplys(GrpcRueckfrage rueckfrage) {
-        return rueckfrage.getAnswersList().stream().map(antwort ->
-                ReplyNachricht.builder()
-                        .message(antwort.getAnswerText())
-                        .id(antwort.getRueckfrageId())
-                        .attachments(antwort.getAnswerAttachmentList().stream().map(att -> OzgFile.builder()
-                                .id(att.getId())
-                                .fileName(att.getName())
-                                .fileSize(att.getSize())
-                                .contentType(att.getContentType()).build()).toList())
-                        .build()).toList();
-    }
-
-    static GrpcOzgFile fromGrpcUploadBinaryFileMetaData(GrpcUploadBinaryFileMetaData fileMetaData, String fileId) {
-        return GrpcOzgFile.newBuilder()
-                .setContentType(fileMetaData.getContentType())
-                .setId(fileId)
-                .setName(fileMetaData.getFileName())
-                .setSize(fileMetaData.getSize()).build();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/E2eTestController.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/E2eTestController.java
deleted file mode 100644
index 9733fc7a3f52d7ffb1c15db9cc10cec1c207fb15..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/E2eTestController.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import de.mgm.bup.ozg.antragsraum.nachricht.Nachricht;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.security.SecurityRequirement;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.UUID;
-
-import static de.mgm.bup.ozg.antragsraum.mocks.GrpcDataCreator.*;
-
-@RestController
-@Log4j2
-@RequestMapping(E2eTestController.PATH)
-@RequiredArgsConstructor
-@SecurityRequirement(name = "security_auth")
-public class E2eTestController {
-    static final String CONTENT_TYPE = "text/plain";
-    private static final String DATA = "Inhalt der Testdatei ";
-    public static final String PATH = "/api/e2e/";
-
-    private final StubDataRepository stubDataRepository;
-    private final WritingNachrichtenEventService nachrichtEventService;
-
-    @Operation(summary = "Create a Nachricht and send it to the InfoManager")
-    @PostMapping(value = "event", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
-    public ResponseEntity<NachrichtEvent> sendNachricht(@RequestBody @Parameter(description = "The Testevent data as JSON") MockEvent mockEvent) {
-
-        var event = nachrichtEventService.addEvent(mockEvent, false);
-        var nachrichtId = createNachricht(event, mockEvent);
-
-        LOG.info("Created Nachricht Id: {}", nachrichtId);
-
-        return ResponseEntity.ok().body(event);
-    }
-
-    String createNachricht(NachrichtEvent nachrichtEvent, MockEvent mockEvent) {
-        var reply = GrpcDataCreator.getRueckfrageBuilder(nachrichtEvent.vorgangId())
-                .setId(nachrichtEvent.rueckfrageId())
-                .setText(StringUtils.isNotEmpty(mockEvent.clerkMessageText()) ? mockEvent.clerkMessageText() : TEXT)
-                .setVorgangName(StringUtils.isNotEmpty(mockEvent.clerkMessageTitle()) ? mockEvent.clerkMessageTitle() : VORGANG_TITLE + nachrichtEvent.vorgangId())
-                .setSendAt(StringUtils.isNotEmpty(mockEvent.sendDateTime()) ? mockEvent.sendDateTime() : LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
-        if (mockEvent.attachmentCount() > 0) {
-            for (int i = 0; i < mockEvent.attachmentCount(); i++) {
-                var fileId = UUID.randomUUID().toString();
-                var fileContent = (DATA + i).getBytes();
-                var file = GrpcBinaryFile.newBuilder()
-                        .setId(fileId)
-                        .setName("TestDoc" + i + ".txt")
-                        .setContentType(CONTENT_TYPE)
-                        .setSize(fileContent.length).build();
-                reply.addAttachment(file);
-
-                stubDataRepository.setFile(file, reply.getVorgangId());
-                stubDataRepository.addFileContent(fileId, fileContent);
-            }
-        }
-        return stubDataRepository.addReply(reply.build(), nachrichtEvent);
-    }
-
-    @Operation(summary = "Finish a Nachricht. This will delete the Nachricht in the InfoManager and remove the Messages linked to it.")
-    @DeleteMapping("event/{nachrichtId}")
-    public ResponseEntity<Void> finishNachricht(@PathVariable @Parameter(description = "The id of the Nachricht to load", example = "6358fd4146811d04010f44d0") String nachrichtId) {
-        nachrichtEventService.finishEvent(nachrichtId);
-        removeNachricht(nachrichtId);
-
-        return ResponseEntity.ok().build();
-    }
-
-    @Operation(summary = "Get the Nachricht stored in memory")
-    @GetMapping("event/{nachrichtId}")
-    public ResponseEntity<Nachricht> getNachricht(@PathVariable @Parameter(description = "The id of the Nachricht to load", example = "6358fd4146811d04010f44d0") String nachrichtId) {
-        var nachricht = stubDataRepository.findNachricht(nachrichtId);
-        LOG.info("Nachricht: " + nachricht);
-        return nachricht.map(value -> ResponseEntity.ok().body(value)).orElseGet(() -> ResponseEntity.notFound().build());
-    }
-
-    void removeNachricht(String nachrichtId) {
-        stubDataRepository.removeNachricht(nachrichtId);
-    }
-
-    @Operation(summary = "Clear all messages and nachrichten and reset the Mocks to initial state")
-    @GetMapping("reset")
-    public ResponseEntity<Void> resetStorage() {
-        stubDataRepository.initEvents();
-        stubDataRepository.reset();
-
-        return ResponseEntity.ok().build();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcDataCreator.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcDataCreator.java
deleted file mode 100644
index a67d9a0fa152d0cb8b20ac11046569cdfe922436..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcDataCreator.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrage;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.springframework.http.MediaType;
-
-import java.nio.charset.StandardCharsets;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-@Log4j2
-class GrpcDataCreator {
-    static final String POSTFACH_ID_1 = "28721c6f-b78f-4d5c-a048-19fd2fc429d2";
-    static final String POSTFACH_ID_2 = "12345c6a-b78f-4d5c-a048-19fd2fc400a1";
-    static final String NACHRICHT_ID_1_0 = "6358fd4146811d04010f44d0";
-    static final String NACHRICHT_ID_2_0 = "6358fd4446811d04010f44da";
-    static final String NACHRICHT_ID_2_1 = "6358fd4446811d04010f44db";
-    static final String VORGANG_ID_1 = "6358fd0bee7a051389cdd787";
-    static final String VORGANG_ID_2 = "6358fd3f46811d04010f44c7";
-    public static final String TEXT = """
-            Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
-             sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
-             sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
-             
-             Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-             Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
-            """;
-    static final String FILE_ID_1 = "6358fd0bee7a051389cdd788";
-    static final String FILE_ID_2 = "6358fd0bee7a051389cdd799";
-    static final String FILE_NAME = "TestDokument.txt";
-    static final byte[] FILE_CONTENT = TEXT.getBytes(StandardCharsets.UTF_8);
-    static final String VORGANG_TITLE = "Vorgang title ";
-
-    static GrpcRueckfrage.Builder getRueckfrageBuilder(String vorgangId) {
-        var builder = GrpcRueckfrage.newBuilder()
-                .setId(RandomStringUtils.randomAlphanumeric(16))
-                .setVorgangId(vorgangId)
-                .setText(TEXT)
-                .setVorgangName(VORGANG_TITLE + vorgangId)
-                .setSendAt("2022-10-26T09:26:25")
-                .clearAnswers();
-        if (vorgangId.equals(VORGANG_ID_1)) {
-            builder.addAttachment(createBinaryFile(FILE_ID_1));
-            builder.setId(NACHRICHT_ID_1_0);
-        } else if (vorgangId.equals(VORGANG_ID_2)) {
-            builder.setId(NACHRICHT_ID_2_0);
-            builder.addAttachment(createBinaryFile(FILE_ID_2));
-        }
-
-        return builder;
-    }
-
-    static GrpcBinaryFile createBinaryFile(String fileId) {
-        return GrpcBinaryFile.newBuilder()
-                .setContentType(MediaType.TEXT_PLAIN_VALUE)
-                .setSize(FILE_CONTENT.length)
-                .setId(fileId)
-                .setName(FILE_NAME)
-                .build();
-    }
-
-    static GrpcOzgFile createFile(String fileId) {
-        return GrpcOzgFile.newBuilder()
-                .setContentType(MediaType.TEXT_PLAIN_VALUE)
-                .setSize(FILE_CONTENT.length)
-                .setId(fileId)
-                .setName(FILE_NAME)
-                .build();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/InformationGrpcServiceStub.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/InformationGrpcServiceStub.java
deleted file mode 100644
index ae35ecbd19d835518139d9815fcaa97403e75234..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/InformationGrpcServiceStub.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.mgm.bup.ozg.antragsraum.infomanager.*;
-import io.grpc.stub.StreamObserver;
-import lombok.RequiredArgsConstructor;
-import net.devh.boot.grpc.server.service.GrpcService;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-
-@GrpcService
-@ConditionalOnProperty(prefix = "grpc", name = "info_service.stub.enabled")
-@RequiredArgsConstructor
-public class InformationGrpcServiceStub extends InformationServiceGrpc.InformationServiceImplBase {
-    private final StubEventRepository stubEventRepository;
-
-    @Override
-    public void getInformation(GrpcInformationRequest request, StreamObserver<GrpcInformationResponse> responseObserver) {
-        var events = stubEventRepository.getNachrichtenEvents(request.getPostfachId());
-        var grpcEvents = events.stream().map(event -> GrpcNachricht.newBuilder()
-                .setPostfachId(event.postfachId())
-                .setNachrichtId(event.rueckfrageId())
-                .setVorgangId(event.vorgangId())
-                .setEventId(event.rueckfrageId()).build()).toList();
-
-        responseObserver.onNext(GrpcInformationResponse.newBuilder().addAllNachrichten(grpcEvents).build());
-
-        responseObserver.onCompleted();
-    }
-
-    @Override
-    public void getServiceUrlOfNachricht(GrpcServiceUrlRequest request, StreamObserver<GrpcServiceUrlResponse> observer) {
-        observer.onNext(GrpcServiceUrlResponse.newBuilder().setUrl("static://localhost:9090").build());
-        observer.onCompleted();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/MockEvent.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/MockEvent.java
deleted file mode 100644
index 98f27adeeac06263766a21583eeaaef7a86a5b3b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/MockEvent.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Builder;
-
-@Builder(toBuilder = true)
-public record MockEvent(
-        @Schema(description = "The postkorbHandle",
-                name = "postkorbHandle",
-                type = "string",
-                example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2"
-        )
-        String postkorbHandle,
-        @Schema(description = "The Vorgang Id",
-                name = "vorgangId",
-                type = "string",
-                example = "abcd1c6f-b78f-4d5c-a048-19fd2fc42xxx"
-        )
-        String vorgangId,
-        @Schema(description = "Address of the OZG-Cloud instance. Should be 'static://localhost:9090' other values will produce errors!",
-                name = "address",
-                type = "string",
-                example = "static://localhost:9090"
-        )
-        String address,
-        @Schema(description = "The Vorgang title of the message send by the clerk. Optional if empty the example title is used.",
-                name = "clerkMessageTitle",
-                type = "string",
-                example = "Vorgang title <id of the reply message>"
-        )
-        String clerkMessageTitle,
-        @Schema(description = "The text of the message send by the clerk. Optional, if empty an example text is used.",
-                name = "clerkMessageText",
-                type = "string",
-                example = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr,\n sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,\\n sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.\\n\\n Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\\n Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
-        )
-        String clerkMessageText,
-
-        @Schema(description = "The timestamp when the clerk message has been send. When not set the current time and date is used",
-                name = "sendDateTime",
-                type = "String",
-                example = "2023-12-01T12:23:25.000"
-        )
-        String sendDateTime,
-        @Schema(description = "Add the test attachments to the event. Defaults to 1, 0 means no attachments",
-                name = "attachmentCount",
-                type = "integer",
-                example = "1")
-        int attachmentCount) {
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/NachrichtenEvenServiceGrpcStub.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/NachrichtenEvenServiceGrpcStub.java
deleted file mode 100644
index 60d0f4e228145bb533096b577d614aa9984c1fd6..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/NachrichtenEvenServiceGrpcStub.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import com.google.protobuf.Empty;
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcFinishedNachrichtRequest;
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcNewNachrichtRequest;
-import de.mgm.bup.ozg.antragsraum.infomanager.NachrichtServiceGrpc;
-import io.grpc.stub.StreamObserver;
-import lombok.RequiredArgsConstructor;
-import net.devh.boot.grpc.server.service.GrpcService;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-
-@GrpcService
-@ConditionalOnProperty(prefix = "grpc", name = "info_service.stub.enabled")
-@RequiredArgsConstructor
-public class NachrichtenEvenServiceGrpcStub extends NachrichtServiceGrpc.NachrichtServiceImplBase {
-    private final StubEventRepository stubEventRepository;
-
-    @Override
-    public void finishNachricht(GrpcFinishedNachrichtRequest request, StreamObserver<Empty> responseObserver) {
-        stubEventRepository.removeNachrichtEvent(request.getNachrichtId());
-
-        responseObserver.onNext(Empty.getDefaultInstance());
-
-        responseObserver.onCompleted();
-    }
-
-    @Override
-    public void saveNewNachricht(GrpcNewNachrichtRequest request, StreamObserver<Empty> responseObserver) {
-        stubEventRepository.addNachrichtEvent(request);
-
-        responseObserver.onNext(Empty.getDefaultInstance());
-
-        responseObserver.onCompleted();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/StubDataRepository.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/StubDataRepository.java
deleted file mode 100644
index bdaa9f22409839bb1f1d626eba01476d00bf365f..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/StubDataRepository.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.mail.postfach.GrpcFindRueckfragenResponse;
-import de.itvsh.ozg.mail.postfach.GrpcGetRueckfrageResponse;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrage;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrageAnswer;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
-import de.mgm.bup.ozg.antragsraum.common.NotFoundException;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import de.mgm.bup.ozg.antragsraum.nachricht.Nachricht;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static de.mgm.bup.ozg.antragsraum.mocks.GrpcDataCreator.*;
-import static de.mgm.bup.ozg.antragsraum.mocks.WritingNachrichtenEventService.*;
-
-@Log4j2
-@Component
-@RequiredArgsConstructor
-class StubDataRepository {
-    private final WritingNachrichtenEventService nachrichtEventService;
-    private final Map<String, List<String>> messageStorage = new ConcurrentHashMap<>();
-    private final Map<String, GrpcRueckfrage> nachrichtStorage = new ConcurrentHashMap<>();
-    private final Map<String, GrpcUploadBinaryFileMetaData> fileStorage = new ConcurrentHashMap<>();
-    private final Map<String, byte[]> fileContentStorage = new ConcurrentHashMap<>();
-
-    private final AtomicBoolean isInitialized = new AtomicBoolean(false);
-
-    public GrpcFindRueckfragenResponse getRueckfragenResponse(String postfachId) {
-        initTestData();
-
-        if (messageStorage.get(postfachId) != null) {
-            var nachrichten = messageStorage.get(postfachId).stream().map(nachrichtStorage::get).toList();
-            return GrpcFindRueckfragenResponse.newBuilder().clearRueckfrage().addAllRueckfrage(nachrichten).build();
-        } else {
-            throw new NotFoundException(GrpcFindRueckfragenResponse.class, postfachId);
-        }
-    }
-
-    public GrpcGetRueckfrageResponse getRueckfrageResponse(String rueckfrageId) {
-        initTestData();
-
-        if (nachrichtStorage.get(rueckfrageId) != null) {
-            var nachricht = nachrichtStorage.get(rueckfrageId);
-            return GrpcGetRueckfrageResponse.newBuilder().setRueckfrage(nachricht).build();
-        } else {
-            throw new NotFoundException(GrpcGetRueckfrageResponse.class, rueckfrageId);
-        }
-    }
-
-    public String addReply(GrpcRueckfrage rueckfrage, NachrichtEvent event) {
-        initTestData();
-
-        nachrichtStorage.put(event.rueckfrageId(), rueckfrage);
-
-        var nachrichtenListOpt = Optional.ofNullable(messageStorage.get(event.postfachId()));
-        nachrichtenListOpt.ifPresentOrElse(nachrichtenList -> {
-            if (!nachrichtenList.contains(event.rueckfrageId())) {
-                var nachrichtenIds = new ArrayList<>(nachrichtenList);
-                nachrichtenIds.add(rueckfrage.getId());
-                messageStorage.put(event.postfachId(), nachrichtenIds);
-            }
-        }, () -> messageStorage.put(event.postfachId(), List.of(rueckfrage.getId())));
-        return rueckfrage.getId();
-    }
-
-    public void removeNachricht(String nachrichtId) {
-        initTestData();
-
-        var entries = messageStorage.entrySet().stream().filter(entry -> entry.getValue().contains(nachrichtId)).toList();
-        entries.forEach(entry -> {
-            var nachrichtenIdList = entry.getValue();
-            var cleanList = nachrichtenIdList.stream().filter(id -> !nachrichtId.equals(id)).toList();
-            nachrichtStorage.remove(nachrichtId);
-            messageStorage.put(entry.getKey(), cleanList);
-        });
-    }
-
-    List<String> getAllNachrichtenIds() {
-        return messageStorage.values().stream().flatMap(List::stream).toList();
-    }
-
-    void addFileContent(String fileId, byte[] content) {
-        fileContentStorage.put(fileId, content);
-    }
-
-    String addFile(GrpcUploadBinaryFileMetaData file) {
-        var fileId = RandomStringUtils.randomAlphanumeric(16);
-        fileStorage.put(fileId, file);
-        return fileId;
-    }
-
-    boolean addReply(GrpcRueckfrageAnswer answer) {
-        boolean success = true;
-
-
-        var reply = nachrichtStorage.get(answer.getRueckfrageId());
-        if (reply != null) {
-            var filledAttachments = answer.getAnswerAttachmentList().stream()
-                    .map(GrpcBinaryFile::getId)
-                    .map(this::getUploadedAnswerAttachment)
-                    .filter(Optional::isPresent)
-                    .map(Optional::get)
-                    .toList();
-            var filledAnswer = answer.toBuilder().clearAnswerAttachment().addAllAnswerAttachment(filledAttachments).build();
-
-            var replyBuilder = reply.toBuilder()
-                    .addAnswers(filledAnswer)
-                    .setAnsweredAt(LocalDateTime.now().toString());
-
-            nachrichtStorage.put(answer.getRueckfrageId(), replyBuilder.build());
-        } else {
-            throw new NotFoundException(GrpcRueckfrageAnswer.class, answer.getRueckfrageId());
-        }
-
-        return success;
-    }
-
-    Optional<GrpcBinaryFile> getUploadedAnswerAttachment(String id) {
-        var fileDataOpt = getFileMetaData(id);
-
-        AtomicReference<Optional<GrpcBinaryFile>> res = new AtomicReference<>(Optional.empty());
-
-        fileDataOpt.ifPresent(fileMetaData -> res.set(Optional.of(GrpcBinaryFile.newBuilder()
-                .setContentType(fileMetaData.getContentType())
-                .setName(fileMetaData.getFileName())
-                .setSize(fileMetaData.getSize())
-                .setId(id).build()))
-        );
-
-        return res.get();
-    }
-
-    Optional<Nachricht> findNachricht(String nachrichtId) {
-        Optional<GrpcRueckfrage> reply = Optional.ofNullable(nachrichtStorage.get(nachrichtId));
-
-        return reply.map(DataMapper::fromGrpcRueckfrage);
-    }
-
-    Optional<GrpcUploadBinaryFileMetaData> getFileMetaData(String fileId) {
-        return Optional.ofNullable(fileStorage.get(fileId));
-    }
-
-    Optional<byte[]> getFileContent(String fileId) {
-        return Optional.ofNullable(fileContentStorage.get(fileId));
-    }
-
-    void initTestData() {
-        if (!isInitialized.get()) {
-            var allNachrichtenIds = new ArrayList<String>();
-
-            allNachrichtenIds.addAll(nachrichtEventService.getEventsOfPostfach(POSTFACH_ID_1));
-            allNachrichtenIds.addAll(nachrichtEventService.getEventsOfPostfach(POSTFACH_ID_2));
-
-            allNachrichtenIds.forEach(nachrichtEventService::finishEvent);
-
-            initEvents();
-        }
-    }
-
-    void initEvents() {
-        nachrichtEventService.finishEvent(NACHRICHT_ID_1_0);
-        nachrichtEventService.finishEvent(NACHRICHT_ID_2_0);
-        nachrichtEventService.finishEvent(NACHRICHT_ID_2_1);
-
-        var nachrichtenIds = getAllNachrichtenIds();
-        LOG.info("Loaded Nachrichten Ids: {}", nachrichtenIds);
-
-        nachrichtenIds.forEach(nachrichtEventService::finishEvent);
-
-        reset();
-
-        nachrichtEventService.addEvent(
-                new MockEvent.MockEventBuilder().postkorbHandle(POSTFACH_ID_1).address(LOCALHOST_9090).build(), true);
-        nachrichtEventService.addEvent(
-                new MockEvent.MockEventBuilder().postkorbHandle(POSTFACH_ID_2).address(LOCALHOST_9090).build(), true);
-
-        isInitialized.set(true);
-    }
-
-    public void reset() {
-        messageStorage.clear();
-        fileStorage.clear();
-
-        nachrichtStorage.put(NACHRICHT_ID_1_0, getRueckfrageBuilder(VORGANG_ID_1).build());
-        messageStorage.put(POSTFACH_ID_1, List.of(NACHRICHT_ID_1_0));
-
-        nachrichtStorage.put(NACHRICHT_ID_2_0, getRueckfrageBuilder(VORGANG_ID_2).build());
-        nachrichtStorage.put(NACHRICHT_ID_2_1, getRueckfrageBuilder(VORGANG_ID_2).build());
-        messageStorage.put(POSTFACH_ID_2, List.of(NACHRICHT_ID_2_0, NACHRICHT_ID_2_1));
-    }
-
-    public void setFile(GrpcBinaryFile file, String vorgangId) {
-        fileStorage.put(file.getId(), GrpcUploadBinaryFileMetaData.newBuilder()
-                .setFileName(file.getName())
-                .setVorgangId(vorgangId)
-                .setSize(file.getSize())
-                .setContentType(file.getContentType())
-                .build());
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/StubEventRepository.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/StubEventRepository.java
deleted file mode 100644
index 40f0f922ec8543d795d3832bb282286f9b495ef6..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/StubEventRepository.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcNewNachrichtRequest;
-import lombok.NonNull;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.stereotype.Component;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-@Log4j2
-@Component
-public class StubEventRepository {
-    public static final String POSTFACH_ID = "28721c6f-b78f-4d5c-a048-19fd2fc429d2";
-    public static final String VORGANG_ID = "6358fd0bee7a051389cdd787";
-    public static final String RUECKFRAGE_ID = "6358fd4146811d04010f44d0";
-    private final Map<String, NachrichtEvent> events = new HashMap<>();
-
-    private final AtomicBoolean isInitialized = new AtomicBoolean(false);
-
-    public void removeNachrichtEvent(String nachrichtId) {
-        var entryOpt = events.entrySet().stream().filter(entry -> entry.getValue().rueckfrageId().equals(nachrichtId)).findFirst();
-
-        entryOpt.ifPresent(entry -> events.remove(entry.getKey()));
-    }
-
-    public void addNachrichtEvent(GrpcNewNachrichtRequest request) {
-        LOG.info("Adding nachricht event: {}", request.getNachricht());
-        var grpcNachricht = request.getNachricht();
-
-        var eventId = StringUtils.isEmpty(grpcNachricht.getEventId()) ? UUID.randomUUID().toString() : grpcNachricht.getEventId();
-
-        events.put(eventId, NachrichtEvent.builder()
-                .vorgangId(grpcNachricht.getVorgangId())
-                .rueckfrageId(grpcNachricht.getNachrichtId())
-                .postfachId(grpcNachricht.getPostfachId())
-                .build());
-
-        isInitialized.set(true);
-    }
-
-    public List<NachrichtEvent> getNachrichtenEvents(@NonNull String postfachId) {
-        if (!isInitialized.get()) {
-            initEvents();
-        }
-        return events.values().stream().filter(nachrichtEvent -> postfachId.equals(nachrichtEvent.postfachId())).toList();
-    }
-
-    public void initEvents() {
-        events.clear();
-
-        events.put(POSTFACH_ID, NachrichtEvent.builder()
-                .vorgangId(VORGANG_ID)
-                .rueckfrageId(RUECKFRAGE_ID)
-                .postfachId(POSTFACH_ID)
-                .build());
-
-        isInitialized.set(true);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/UploadStreamObserverStub.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/UploadStreamObserverStub.java
deleted file mode 100644
index 8817f083df1c7bc91a3d8baffe98c6d7cbf4b306..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/UploadStreamObserverStub.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileResponse;
-import io.grpc.stub.StreamObserver;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@Log4j2
-public class UploadStreamObserverStub implements StreamObserver<GrpcUploadBinaryFileRequest> {
-    private final StreamObserver<GrpcUploadBinaryFileResponse> responseObserver;
-    private final StubDataRepository stubDataRepository;
-    private ByteArrayOutputStream byteArrayOutput;
-    private CompletableFuture<String> fileIdFuture;
-    private String fileId;
-    private String fileName;
-
-    UploadStreamObserverStub(StreamObserver<GrpcUploadBinaryFileResponse> responseObserver, StubDataRepository stubDataRepository) {
-        this.responseObserver = responseObserver;
-        this.stubDataRepository = stubDataRepository;
-    }
-
-    @Override
-    public void onNext(GrpcUploadBinaryFileRequest fileUploadRequest) {
-        if (fileUploadRequest.hasMetadata()) {
-            LOG.info("Received file metadata: " + fileUploadRequest.getMetadata());
-            fileName = fileUploadRequest.getMetadata().getFileName();
-            fileId = stubDataRepository.addFile(fileUploadRequest.getMetadata());
-            fileIdFuture = CompletableFuture.completedFuture(fileId);
-            byteArrayOutput = new ByteArrayOutputStream();
-        } else {
-            storeFileContent(fileUploadRequest);
-        }
-    }
-
-    void storeFileContent(GrpcUploadBinaryFileRequest fileUploadRequest) {
-        try {
-            IOUtils.write(fileUploadRequest.getFileContent().toByteArray(), byteArrayOutput);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public void onError(Throwable t) {
-        LOG.error("GrpcLargeFileRequestObserver onError", t);
-        throw new RuntimeException("GrpcLargeFileRequestObserver onError has been called", t);
-    }
-
-    @Override
-    public void onCompleted() {
-        try {
-            var fileContent = byteArrayOutput.toByteArray();
-            stubDataRepository.addFileContent(fileId, fileContent);
-            var file = File.createTempFile("upload-", fileName);
-            FileUtils.writeByteArrayToFile(file, fileContent);
-            LOG.info("Saved file {}", file.getAbsoluteFile());
-            responseObserver.onNext(GrpcUploadBinaryFileResponse.newBuilder().setFileId(fileIdFuture.get(3, TimeUnit.SECONDS)).build());
-
-            byteArrayOutput.close();
-        } catch (ExecutionException | IOException | TimeoutException e) {
-            handleException(e);
-        } catch (InterruptedException ie) {
-            handleException(ie);
-            Thread.currentThread().interrupt();
-        }
-        responseObserver.onCompleted();
-
-    }
-
-    void handleException(Exception e) {
-        responseObserver.onNext(GrpcUploadBinaryFileResponse.newBuilder().build());
-        throw new RuntimeException(e.getMessage(), e);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/WritingNachrichtenEventService.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/WritingNachrichtenEventService.java
deleted file mode 100644
index 3d27b9c68aecf20772b276b4d60187d3c49f53b7..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/WritingNachrichtenEventService.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import de.mgm.bup.ozg.antragsraum.infomanager.*;
-import lombok.extern.log4j.Log4j2;
-import net.devh.boot.grpc.client.inject.GrpcClient;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-import java.util.Objects;
-
-import static de.mgm.bup.ozg.antragsraum.mocks.GrpcDataCreator.*;
-
-@Service
-@Log4j2
-public class WritingNachrichtenEventService {
-    public static final String LOCALHOST_9090 = "static://localhost:9090";
-    @GrpcClient("info_manager")
-    private NachrichtServiceGrpc.NachrichtServiceBlockingStub nachrichtServiceBlockingStub;
-
-    @GrpcClient("info_manager")
-    private InformationServiceGrpc.InformationServiceBlockingStub informationServiceBlockingStub;
-
-    public NachrichtEvent addEvent(MockEvent event, boolean isInit) {
-        String listUrl = event.address() != null ? event.address() : LOCALHOST_9090;
-        String vorgangId = getVorgangId(event, isInit);
-        String nachrichtId = getNachrichtId(event, isInit);
-        String postfachId = Objects.nonNull(event.postkorbHandle()) ? event.postkorbHandle() : RandomStringUtils.randomAlphanumeric(16);
-
-        var nachricht = GrpcNachricht.newBuilder()
-                .setNachrichtenListUrl(listUrl)
-                .setVorgangId(vorgangId)
-                .setNachrichtId(nachrichtId)
-                .setPostfachId(postfachId)
-                .build();
-        var res = nachrichtServiceBlockingStub.saveNewNachricht(GrpcNewNachrichtRequest.newBuilder()
-                .setNachricht(nachricht).build());
-
-        LOG.info("Saved NachrichtEvent: {}, Result: {}", nachricht, res);
-        return NachrichtEvent.builder()
-                .rueckfrageId(nachrichtId)
-                .postfachId(postfachId)
-                .vorgangId(vorgangId).build();
-    }
-
-    String getVorgangId(MockEvent event, boolean isInit) {
-        String res;
-        if (isInit && event.postkorbHandle().equals(POSTFACH_ID_1)) {
-            res = VORGANG_ID_1;
-        } else if (isInit && event.postkorbHandle().equals(POSTFACH_ID_2)) {
-            res = VORGANG_ID_2;
-        } else {
-            res = Objects.nonNull(event.vorgangId()) ? event.vorgangId() : RandomStringUtils.randomAlphanumeric(16);
-        }
-        return res;
-    }
-
-    String getNachrichtId(MockEvent event, boolean isInit) {
-        String res;
-        if (isInit && event.postkorbHandle().equals(POSTFACH_ID_1)) {
-            res = NACHRICHT_ID_1_0;
-        } else if (isInit && event.postkorbHandle().equals(POSTFACH_ID_2)) {
-            res = NACHRICHT_ID_2_0;
-        } else {
-            res = RandomStringUtils.randomAlphanumeric(16);
-        }
-        return res;
-    }
-
-    public void finishEvent(String nachrichtId) {
-        var res = nachrichtServiceBlockingStub.finishNachricht(GrpcFinishedNachrichtRequest.newBuilder()
-                .setNachrichtId(nachrichtId)
-                .build());
-
-        LOG.info("Finishing NachrichtEvent Id: {}, Result: {}", nachrichtId, res);
-    }
-
-    public List<String> getEventsOfPostfach(String postfachId) {
-        var res = informationServiceBlockingStub.getInformation(GrpcInformationRequest.newBuilder().setPostfachId(postfachId).build());
-
-        return res.getNachrichtenList().stream().map(GrpcNachricht::getNachrichtId).toList();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/Nachricht.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/Nachricht.java
deleted file mode 100644
index e61fe6e7444696441aa6a30a89cd29201e9c706f..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/Nachricht.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import de.mgm.bup.ozg.antragsraum.attachments.OzgFile;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Min;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.Size;
-import lombok.Builder;
-import org.springframework.validation.annotation.Validated;
-
-import java.util.List;
-
-@JsonInclude(JsonInclude.Include.NON_NULL)
-@Builder(toBuilder = true)
-@Validated
-public record Nachricht(@NotBlank
-                        @Schema(description = "The id of the Rueckfrage",
-                                name = "id",
-                                type = "string",
-                                example = "1258ad3f46811d04010f22ab"
-                        )
-                        String id,
-                        @Min(0)
-                        @Schema(description = "The date the Rueckfrage has been send.")
-                        long date,
-                        @Size(max = 2048, message = "title too long")
-                        @Schema(description = "The title of the topic. This is the title of the Vorgang",
-                                name = "topicTitle",
-                                type = "string",
-                                example = "Vorgang Hundesteuer Antrag"
-                        )
-                        String topicTitle,
-                        @Size(max = 500000, message = "message too long")
-                        String message,
-                        @Size(max = 64, message = "postfach id to long")
-                        @Schema(description = "The id of the postfach. For BayernID this is the PostkorbHandle",
-                                name = "postfachId",
-                                type = "string",
-                                example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2"
-                        )
-                        String postfachId,
-                        @Size(max = 64, message = "vorgang id to long")
-                        @Schema(description = "The id of the Vorgang the Rueckfrage belongs to",
-                                name = "vorgangId",
-                                type = "string",
-                                example = "8523ad3f46811d04010f6654"
-                        )
-                        String vorgangId,
-                        @Schema(description = "The reply option, not used right now always set 'ALLOWED'",
-                                name = "vorgangId",
-                                type = "ReplyOption",
-                                example = "ALLOWED"
-                        )
-                        ReplyOption replyOption,
-                        @Schema(description = "The list of the attached files.")
-                        List<OzgFile> attachments,
-                        @Schema(hidden = true)
-                        List<ReplyNachricht> replyNachrichten) {
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtMapper.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtMapper.java
deleted file mode 100644
index 914970e4e312c2e269a23708c8cc9f508278a0e3..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtMapper.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrage;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrageAnswer;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import de.mgm.bup.ozg.antragsraum.attachments.OzgFile;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-
-import java.time.DateTimeException;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-@Log4j2
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-class NachrichtMapper {
-    public static Nachricht fromGrpcRueckfrage(GrpcRueckfrage grpcRueckfrage) {
-        var builder = Nachricht.builder()
-                .vorgangId(grpcRueckfrage.getVorgangId())
-                .id(grpcRueckfrage.getId())
-                .topicTitle(grpcRueckfrage.getVorgangName())
-                .message(grpcRueckfrage.getText())
-                .replyNachrichten(fromGrpcRueckfrageAnswer(grpcRueckfrage))
-                .date(getUnixTimestamp(grpcRueckfrage.getSendAt()))
-                .attachments(getAttachments(grpcRueckfrage));
-
-        return builder.build();
-    }
-
-    private static List<OzgFile> getAttachments(GrpcRueckfrage grpcRueckfrage) {
-        var ozgFiles = new ArrayList<OzgFile>();
-        grpcRueckfrage.getAttachmentList().forEach(file ->
-                ozgFiles.add(AttachmentMapper.fromGrpcBinaryFile(file))
-        );
-        return ozgFiles;
-    }
-
-    private static List<ReplyNachricht> fromGrpcRueckfrageAnswer(GrpcRueckfrage rueckfrage) {
-        return rueckfrage.getAnswersList().stream()
-                .map(answer -> {
-                    var builder = ReplyNachricht.builder()
-                            .id(rueckfrage.getId())
-                            .message(answer.getAnswerText())
-                            .date(getUnixTimestamp(rueckfrage.getAnsweredAt()))
-                            .attachments(answer.getAnswerAttachmentList().stream()
-                                    .map(AttachmentMapper::fromGrpcBinaryFile).toList());
-                    return builder.build();
-                }).toList();
-    }
-
-    private static long getUnixTimestamp(String dateString) {
-        long timestamp = 0;
-        try {
-            timestamp = LocalDateTime.parse(dateString).toEpochSecond(ZoneOffset.UTC);
-        } catch (DateTimeException e) {
-            LOG.warn("Invalid send date [{}] received for Nachricht. Setting it to 0 (=1970-01-01)", dateString);
-        }
-        return timestamp;
-    }
-
-    public static GrpcRueckfrageAnswer toGrpcRueckfrageAnswer(ReplyNachricht nachricht) {
-        var builder = GrpcRueckfrageAnswer.newBuilder()
-                .setRueckfrageId(nachricht.id())
-                .setAnswerText(nachricht.message());
-
-        if (Objects.nonNull(nachricht.attachments())) {
-            builder.addAllAnswerAttachment(nachricht.attachments().stream()
-                    .map(file -> GrpcBinaryFile.newBuilder()
-                            .setId(file.id())
-                            .build()).toList());
-        }
-        return builder.build();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenController.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenController.java
deleted file mode 100644
index 6b4a9b178f4247ac1a95906b64f0c84813986e95..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenController.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.security.SecurityRequirement;
-import jakarta.validation.Valid;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-@RestController
-@RequestMapping(NachrichtenController.PATH)
-@RequiredArgsConstructor
-@SecurityRequirement(name = "security_auth")
-public class NachrichtenController {
-    public static final String PATH = "/api";
-
-    private final @NonNull NachrichtenService service;
-
-    @Operation(summary = "Load all Nachrichten of a Postfach")
-    @GetMapping(value = "/nachrichten/{postfachId}", produces = MediaType.APPLICATION_JSON_VALUE)
-    public List<Topic> getTopicsOfPostfach(@Parameter(description = "The id of the postfach", example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2") @PathVariable String postfachId) {
-        return service.getTopicsOfPostfach(postfachId);
-    }
-
-    @Operation(summary = "Send the reply to the Rueckfrage into the ozg cloud")
-    @PutMapping(value = "/nachricht/{rueckfrageId}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
-    public ReplyNachricht sendNachricht(
-            @Parameter(description = "The id of the rueckfrage the clerk created", example = "6358fd4146811d04010f44d0") @PathVariable @NonNull String rueckfrageId,
-            @Parameter(description = "The Nachricht content as JSON") @RequestBody @Valid ReplyNachricht nachricht) {
-        return service.sendRueckfrageAnswer(nachricht, rueckfrageId);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenGrpcClient.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenGrpcClient.java
deleted file mode 100644
index d7390f911ab198b5b7f27e644f0f19a21eb378da..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenGrpcClient.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.*;
-import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCommand;
-import de.itvsh.ozg.pluto.grpc.command.GrpcGetCommandRequest;
-import de.mgm.bup.ozg.antragsraum.common.ChannelPropertiesFactory;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
-import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
-import org.springframework.stereotype.Component;
-
-@Log4j2
-@Component
-@RequiredArgsConstructor
-public class NachrichtenGrpcClient {
-    private final @NonNull GlobalClientInterceptorRegistry registry;
-    private final @NonNull ChannelPropertiesFactory channelPropertiesFactory;
-
-    GrpcFindRueckfragenResponse getFindRueckfragen(GrpcFindRueckfragenRequest request, String address) {
-        try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
-            var channel = channelFactory.createChannel(address);
-            var postfachServiceBlockingStub = PostfachServiceGrpc.newBlockingStub(channel);
-
-            return getGrpcFindRueckfragenResponse(request, postfachServiceBlockingStub);
-        }
-    }
-
-    GrpcGetRueckfrageResponse getRueckfrage(GrpcGetRueckfrageRequest request, String address) {
-        try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
-            var channel = channelFactory.createChannel(address);
-            var postfachServiceBlockingStub = PostfachServiceGrpc.newBlockingStub(channel);
-
-            return getGrpcGetRueckfrageResponse(request, postfachServiceBlockingStub);
-        }
-    }
-
-    GrpcFindRueckfragenResponse getGrpcFindRueckfragenResponse(GrpcFindRueckfragenRequest request, PostfachServiceGrpc.PostfachServiceBlockingStub postfachServiceBlockingStub) {
-        return postfachServiceBlockingStub.findRueckfragen(request);
-    }
-
-    GrpcGetRueckfrageResponse getGrpcGetRueckfrageResponse(GrpcGetRueckfrageRequest request, PostfachServiceGrpc.PostfachServiceBlockingStub postfachServiceBlockingStub) {
-        return postfachServiceBlockingStub.getRueckfrage(request);
-    }
-
-    GrpcCommand answerRueckfrage(GrpcRueckfrageAnswerRequest request, String address) {
-        try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
-            var channel = channelFactory.createChannel(address);
-            var postfachServiceBlockingStub = PostfachServiceGrpc.newBlockingStub(channel);
-
-            return getAnswerResponse(request, postfachServiceBlockingStub);
-        }
-    }
-
-    GrpcCommand getAnswerResponse(GrpcRueckfrageAnswerRequest request, PostfachServiceGrpc.PostfachServiceBlockingStub postfachServiceBlockingStub) {
-        return postfachServiceBlockingStub.sendRueckfrageAnswer(request);
-    }
-
-    GrpcCommand getCommand(String address, String commandId) {
-        try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
-            var channel = channelFactory.createChannel(address);
-            var commandServiceBlockingStub = CommandServiceGrpc.newBlockingStub(channel);
-
-            return getGrpcCommand(commandServiceBlockingStub, GrpcGetCommandRequest.newBuilder().setId(commandId).build());
-        }
-    }
-
-    GrpcCommand getGrpcCommand(CommandServiceGrpc.CommandServiceBlockingStub commandServiceBlockingStub, GrpcGetCommandRequest request) {
-        return commandServiceBlockingStub.getCommand(request);
-    }
-
-    GrpcChannelFactory getChannelFactory(String channelAddress) {
-        return de.mgm.bup.ozg.antragsraum.common.GrpcChannelFactory.getChannelFactory(channelPropertiesFactory.getGrpcChannelsProperties(channelAddress), registry);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteService.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteService.java
deleted file mode 100644
index bf5f033b4bbfd70f5711e04a7c148e0a89685abc..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteService.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.GrpcGetRueckfrageRequest;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrageAnswerRequest;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCommand;
-import de.mgm.bup.ozg.antragsraum.common.SendTimeoutException;
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import io.grpc.StatusRuntimeException;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-import java.util.concurrent.*;
-
-@Service
-@Log4j2
-@RequiredArgsConstructor
-class NachrichtenRemoteService {
-    public static final String STATUS_FINISHED = "FINISHED";
-    public static final boolean COMMAND_FINISHED = true;
-
-    private final @NonNull NachrichtenGrpcClient grpcClient;
-    private final @NonNull NachrichtProperties properties;
-
-    List<Nachricht> getRueckfragen(NachrichtEvent event, String address) {
-        try {
-            var reply = grpcClient.getRueckfrage(GrpcGetRueckfrageRequest.newBuilder().
-                    setRueckfrageId(event.rueckfrageId()).build(), address);
-
-            return List.of(NachrichtMapper.fromGrpcRueckfrage(reply.getRueckfrage()));
-        } catch (StatusRuntimeException e) {
-            LOG.error("Error loading nachricht of id " + event.rueckfrageId(), e);
-        }
-
-        return List.of();
-    }
-
-    boolean sendAnswer(ReplyNachricht nachricht, String address) {
-        boolean success;
-
-        GrpcCommand response = grpcClient.answerRueckfrage(
-                GrpcRueckfrageAnswerRequest.newBuilder()
-                        .setAnswer(NachrichtMapper.toGrpcRueckfrageAnswer(nachricht)).build(), address);
-
-        if (STATUS_FINISHED.equals(response.getStatus())) {
-            success = COMMAND_FINISHED;
-        } else {
-            success = waitUntilCommandIsFinished(address, response.getId());
-        }
-
-        return success;
-    }
-
-    boolean waitUntilCommandIsFinished(String address, String commandId) {
-        var auth = SecurityContextHolder.getContext().getAuthentication();
-        ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
-        Future<Boolean> future = executor.submit(() -> checkCommandIsFinished(address, commandId, auth));
-        try {
-            return getSendResult(future);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new TechnicalException(e);
-        } catch (ExecutionException e) {
-            throw new TechnicalException(e);
-        } catch (TimeoutException e) {
-            throw new SendTimeoutException(commandId, e);
-        } finally {
-            executor.shutdown();
-        }
-    }
-
-    Boolean checkCommandIsFinished(String address, String commandId, Authentication auth) throws InterruptedException {
-        SecurityContextHolder.getContext().setAuthentication(auth);
-        
-        boolean notFinished = true;
-        while (notFinished) {
-            var resCommand = grpcClient.getCommand(address, commandId);
-            notFinished = !STATUS_FINISHED.equals(resCommand.getStatus());
-
-            TimeUnit.MILLISECONDS.sleep(properties.getSendPollIntervalDuration().toMillis());
-        }
-
-        return COMMAND_FINISHED;
-    }
-
-    Boolean getSendResult(Future<Boolean> future) throws InterruptedException, ExecutionException, TimeoutException {
-        return future.get(properties.getTimeoutDuration().toMillis(), TimeUnit.MILLISECONDS);
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenService.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenService.java
deleted file mode 100644
index 44946e794d4f0e3c583f18e9be678fc6fee768f6..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenService.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEventService;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.stereotype.Service;
-
-import java.util.Collection;
-import java.util.List;
-
-@Log4j2
-@Service
-@RequiredArgsConstructor
-class NachrichtenService {
-    private final @NonNull NachrichtEventService nachrichtEventService;
-    private final @NonNull NachrichtenRemoteService nachrichtenRemoteService;
-
-    Collection<Nachricht> getRueckfragenOfPostfach(String postfachId) {
-        var events = nachrichtEventService.getNachrichtEventsOfPostfachId(postfachId);
-
-        return events.stream().map(this::getNachrichtenOfEvent).flatMap(List::stream).toList();
-    }
-
-    private List<Nachricht> getNachrichtenOfEvent(final NachrichtEvent event) {
-        var address = nachrichtEventService.getServiceUrlOfNachricht(event.rueckfrageId());
-
-        return nachrichtenRemoteService.getRueckfragen(event, address);
-    }
-
-    ReplyNachricht sendRueckfrageAnswer(final ReplyNachricht nachricht, final @NonNull String replyToId) {
-        var address = nachrichtEventService.getServiceUrlOfNachricht(replyToId);
-
-        nachrichtenRemoteService.sendAnswer(nachricht, address);
-
-        return nachricht;
-    }
-
-    public List<Topic> getTopicsOfPostfach(String postfachId) {
-        return TopicMapper.toTopics(getRueckfragenOfPostfach(postfachId));
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyNachricht.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyNachricht.java
deleted file mode 100644
index f5e5f25a0374bc698c0f72dfad0c68647bdc09ce..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyNachricht.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import de.mgm.bup.ozg.antragsraum.attachments.OzgFile;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Min;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.Size;
-import lombok.Builder;
-import org.springframework.validation.annotation.Validated;
-
-import java.util.List;
-
-@JsonInclude(JsonInclude.Include.NON_NULL)
-@Builder(toBuilder = true)
-@Validated
-public record ReplyNachricht(@NotBlank
-                             @Schema(description = "The id of the Rueckfrage. Is the same as the id of the clerk message",
-                                     name = "id",
-                                     type = "string",
-                                     example = "6358fd4146811d04010f44d0"
-                             )
-                             String id,
-                             @Schema(description = "The answer to the rueckfrage.",
-                                     name = "message",
-                                     type = "string",
-                                     example = "Im Anhang sind die Dokumente"
-                             )
-                             @Size(max = 500000, message = "message too long")
-                             String message,
-                             @Schema(description = "The answered date. Only set when displaying an already answered Rueckfrage. Will be set by the OZG-Cloud")
-                             @Min(0)
-                             long date,
-                             @Schema(description = "The list of the attached file ids. Only the file id is used when sending the reply, The other values are set by the backend")
-                             List<OzgFile> attachments) {
-}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/TopicMapper.java b/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/TopicMapper.java
deleted file mode 100644
index f8ae41d75bc215c81994b6b41d4dfc2158a49fed..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/TopicMapper.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-
-import java.util.Collection;
-import java.util.List;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-class TopicMapper {
-    static List<Topic> toTopics(Collection<Nachricht> nachrichten) {
-        return nachrichten.stream().map(TopicMapper::toTopic).toList();
-    }
-
-    static Topic toTopic(Nachricht nachricht) {
-        var clerkMessage = nachricht.toBuilder().replyNachrichten(null).build();
-        return Topic.builder()
-                .topicTitle(nachricht.topicTitle())
-                .clerkMessage(clerkMessage)
-                .userMessages(createUserNachricht(nachricht.replyNachrichten()))
-                .build();
-    }
-
-    static List<ReplyNachricht> createUserNachricht(List<ReplyNachricht> replyNachrichten) {
-        return replyNachrichten.stream().map(replyNachricht -> ReplyNachricht.builder().id(replyNachricht.id())
-                .message(replyNachricht.message())
-                .date(replyNachricht.date())
-                .attachments(replyNachricht.attachments())
-                .build()).toList();
-    }
-}
diff --git a/ozg-antragsraum-server/src/main/resources/application-ci.properties b/ozg-antragsraum-server/src/main/resources/application-ci.properties
deleted file mode 100644
index 90a243b8761dc7b135f9edac327e04b7c259a0de..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/resources/application-ci.properties
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# 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.
-#
-spring.application.name=Antragsraum
-spring.mvc.pathmatch.matching-strategy=ant_path_matcher
-spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
-# max-file-size must be larger that the actual file limit because of possible transmission overhead
-spring.servlet.multipart.max-file-size=150MB
-spring.servlet.multipart.max-request-size=2GB
-management.server.port=8083
-management.endpoints.enabled-by-default=false
-management.endpoint.info.enabled=true
-management.endpoint.health.enabled=true
-management.endpoints.web.exposure.include=health,info
-grpc.client.info_manager.address=static://127.0.0.1:9091
-grpc.client.info_manager.negotiation-type=PLAINTEXT
-grpc.info_service.stub.enabled=false
-grpc.ozg_service.stubs.enabled=true
-ozgcloud.grpc.client.negotiation-type=PLAINTEXT
-testfile.path.prefix=src/test/resources/testfiles/
-logging.level.org.springframework.security=DEBUG
-logging.level.de.mgm.bup.ozg.antragsraum=DEBUG
-# File size limit used by the application
-ozgcloud.upload.max-file-size=100MB
-# Send Nachricht timeout as ISO-8601 duration format PnDTnHnMn.nS
-ozgcloud.nachricht.send.timeout=60s
-# Send Nachricht poll interval as ISO-8601 duration format PnDTnHnMn.nS
-ozgcloud.nachricht.send.poll.interval=2s
-spring.security.oauth2.resourceserver.jwt.issuer-uri=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci
-spring.security.oauth2.client.registration.keycloak.client-id=antragsraum-api
-spring.security.oauth2.client.registration.keycloak.client-secret=aAn3H2T9xl1nFKGQwVa5EoznoogYrnEd
-spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
-spring.security.oauth2.client.registration.keycloak.scope=profile
-spring.security.oauth2.client.provider.keycloak.issuer-uri=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci
-spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
-# swagger properties
-springdoc.swagger-ui.oauth.client-id=antragsraum-api
-springdoc.swagger-ui.oauth.client-secret=aAn3H2T9xl1nFKGQwVa5EoznoogYrnEd
-springdoc.oAuthFlow.authorizationUrl=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci/protocol/openid-connect/auth
-springdoc.oAuthFlow.tokenUrl=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci/protocol/openid-connect/token
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/main/resources/application-dev.properties b/ozg-antragsraum-server/src/main/resources/application-dev.properties
deleted file mode 100644
index 5e62db37eb5add2ee50090be1a1da180d6812a6c..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/resources/application-dev.properties
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# 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.
-#
-spring.application.name=Antragsraum
-spring.mvc.pathmatch.matching-strategy=ant_path_matcher
-spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
-spring.servlet.multipart.max-file-size=150MB
-spring.servlet.multipart.max-request-size=2GB
-management.server.port=8083
-grpc.client.info_manager.address=static://127.0.0.1:9091
-grpc.client.info_manager.negotiation-type=PLAINTEXT
-grpc.info_service.stub.enabled=false
-grpc.ozg_service.stubs.enabled=true
-ozgcloud.grpc.client.negotiation-type=PLAINTEXT
-testfile.path.prefix=src/test/resources/testfiles/
-logging.level.org.springframework.security=DEBUG
-logging.level.de.mgm.bup.ozg.antragsraum=DEBUG
-springdoc.swagger-ui.csrf.enabled=true
-# ClamAV properties
-clamav.server.host=localhost
-clamav.server.port=3310
diff --git a/ozg-antragsraum-server/src/main/resources/application-standalone.properties b/ozg-antragsraum-server/src/main/resources/application-standalone.properties
deleted file mode 100644
index 6229225b2b86d252f5dd83b3b389fe1e88b2984a..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/resources/application-standalone.properties
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# 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.
-#
-spring.application.name=Antragsraum
-spring.mvc.pathmatch.matching-strategy=ant_path_matcher
-spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
-spring.servlet.multipart.max-file-size=150MB
-spring.servlet.multipart.max-request-size=2GB
-management.server.port=8084
-grpc.client.info_manager.address=static://127.0.0.1:9090
-grpc.client.info_manager.negotiation-type=PLAINTEXT
-grpc.info_service.stub.enabled=true
-grpc.ozg_service.stubs.enabled=true
-ozgcloud.grpc.client.negotiation-type=PLAINTEXT
-testfile.path.prefix=src/test/resources/testfiles/
-logging.level.org.springframework.security=DEBUG
-# ClamAV properties
-clamav.server.host=localhost
-clamav.server.port=3310
-# test
-spring.security.oauth2.resourceserver.jwt.issuer-uri=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci
-spring.security.oauth2.client.registration.keycloak.client-id=antragsraum-api
-spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
-spring.security.oauth2.client.registration.keycloak.scope=profile
-spring.security.oauth2.client.provider.keycloak.issuer-uri=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci
-spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
-springdoc.swagger-ui.oauth.client-id=antragsraum-api
-springdoc.swagger-ui.oauth.client-secret=aAn3H2T9xl1nFKGQwVa5EoznoogYrnEd
-springdoc.oAuthFlow.authorizationUrl=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci/protocol/openid-connect/auth
-springdoc.oAuthFlow.tokenUrl=https://bup-ozg-ci-keycloak.pidev.mgm-tp.com/realms/ozg-ci/protocol/openid-connect/token
diff --git a/ozg-antragsraum-server/src/main/resources/application.properties b/ozg-antragsraum-server/src/main/resources/application.properties
deleted file mode 100644
index 2b7f39fea3e68550f554160f760bedb9b30c8b08..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/main/resources/application.properties
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# 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.
-#
-spring.application.name=Antragsraum
-spring.mvc.pathmatch.matching-strategy=ant_path_matcher
-spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
-# max-file-size must be larger that the actual file limit because of possible transmission overhead
-spring.servlet.multipart.max-file-size=150MB
-spring.servlet.multipart.max-request-size=2GB
-server.forward-headers-strategy=framework
-management.server.port=8081
-management.endpoints.enabled-by-default=false
-management.endpoint.info.enabled=true
-management.endpoint.health.enabled=true
-management.endpoints.web.exposure.include=health,info
-grpc.client.info_manager.address=static://127.0.0.1:9091
-grpc.client.info_manager.negotiation-type=PLAINTEXT
-# File size limit used by the application
-ozgcloud.upload.max-file-size=100MB
-# Send Nachricht timeout as ISO-8601 duration format PnDTnHnMn.nS
-ozgcloud.nachricht.send.timeout=60s
-# Send Nachricht poll interval as ISO-8601 duration format PnDTnHnMn.nS
-ozgcloud.nachricht.send.poll.interval=2s
-spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8081/realms/ozg-ci
-spring.security.oauth2.client.registration.keycloak.client-id=antragsraum-api
-spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
-spring.security.oauth2.client.registration.keycloak.scope=profile
-spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8081/realms/ozg-ci
-spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
-#swagger properties
-springdoc.swagger-ui.oauth.client-id=antragsraum-api
-springdoc.swagger-ui.oauth.client-secret=6kEHgIBJq9lQrobmqMJ7Ui1Q425otsb0
-springdoc.oAuthFlow.authorizationUrl=${OAUTH2_SERVER:http://localhost:8081}/realms/ozg-ci/protocol/openid-connect/auth
-springdoc.oAuthFlow.tokenUrl=${OAUTH2_SERVER:http://localhost:8081}/realms/ozg-ci/protocol/openid-connect/token
-springdoc.swagger-ui.csrf.enabled=true
-# ClamAV properties
-clamav.server.host=localhost
-clamav.server.port=3310
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/Oauth2TestConfiguration.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/Oauth2TestConfiguration.java
deleted file mode 100644
index 4ec39e4e14617a3988ceb91d22940b2e492cdc06..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/Oauth2TestConfiguration.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.convert.converter.Converter;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-@Configuration
-public class Oauth2TestConfiguration {
-    interface JwtAuthoritiesConverter extends Converter<Jwt, Collection<GrantedAuthority>> {
-    }
-
-    @Bean
-    JwtAuthoritiesConverter realmRoles2AuthoritiesConverter() {
-        return (Jwt jwt) -> {
-            final var realmRoles = Optional.of(jwt.getClaimAsMap("realm_access")).orElse(Map.of());
-            @SuppressWarnings("unchecked") final var roles = (List<String>) realmRoles.getOrDefault("roles", List.of());
-            return roles.stream().map(SimpleGrantedAuthority::new).map(GrantedAuthority.class::cast).toList();
-        };
-    }
-
-    @Bean
-    JwtAuthenticationConverter authenticationConverter(Converter<Jwt, Collection<GrantedAuthority>> authoritiesConverter) {
-        final var authenticationConverter = new JwtAuthenticationConverter();
-        authenticationConverter.setPrincipalClaimName(StandardClaimNames.PREFERRED_USERNAME);
-        authenticationConverter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
-        return authenticationConverter;
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/SecurityConfigurationTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/SecurityConfigurationTest.java
deleted file mode 100644
index f656a02c12b05ad03075b110a8057fcbb74620ac..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/SecurityConfigurationTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-class SecurityConfigurationTest {
-    private final SecurityConfiguration securityConfiguration = new SecurityConfiguration();
-
-    @Test
-    void shouldCreateSessionAuthenticationStrategy() {
-        var sessionAuthenticationStrategy = securityConfiguration.sessionAuthenticationStrategy();
-
-        assertThat(sessionAuthenticationStrategy).isNotNull().isInstanceOf(NullAuthenticatedSessionStrategy.class);
-    }
-
-    @Nested
-    class TestHttpSecurity {
-        private HttpSecurity httpSecurity;
-
-        @BeforeEach
-        void init() throws Exception {
-            httpSecurity = mock(HttpSecurity.class);
-            when(httpSecurity.authorizeHttpRequests(any())).thenReturn(httpSecurity);
-        }
-
-        @Test
-        void shouldSetupFilterChainAuthorize() throws Exception {
-            securityConfiguration.filterChain(httpSecurity);
-
-            verify(httpSecurity).authorizeHttpRequests(any());
-        }
-
-        @Test
-        void shouldSetupFilterChainOauthResourceServer() throws Exception {
-            securityConfiguration.filterChain(httpSecurity);
-
-            verify(httpSecurity).oauth2ResourceServer(any());
-        }
-
-        @Test
-        void shouldGetAuthenticationManager() throws Exception {
-            var authManagerBuilder = mock(AuthenticationManagerBuilder.class);
-            when(httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)).thenReturn(authManagerBuilder);
-            securityConfiguration.authenticationManager(httpSecurity);
-
-            verify(authManagerBuilder).build();
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/ChunkedFileSenderTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/ChunkedFileSenderTest.java
deleted file mode 100644
index 22f8c5e233c6122bca823539f8069282018542b3..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/ChunkedFileSenderTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest;
-import io.grpc.stub.CallStreamObserver;
-import lombok.SneakyThrows;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.test.util.ReflectionTestUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Function;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-class ChunkedFileSenderTest {
-    @SuppressWarnings("unchecked")
-    private final CallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver = mock(CallStreamObserver.class);
-    private final InputStream uploadStream = mock(InputStream.class);
-    private final int chunkSize = OzgFileTestFactory.DATA.length;
-    private final GrpcUploadBinaryFileRequest uploadBinaryFileRequest = GrpcUploadBinaryFileRequestTestFactory.createDataRequest();
-
-    @SuppressWarnings("unchecked")
-    private final Function<byte[], GrpcUploadBinaryFileRequest> buildUploadBinaryFileRequest = when(mock(Function.class).apply(any()))
-            .thenReturn(uploadBinaryFileRequest).getMock();
-    private final GrpcUploadBinaryFileRequest metadataRequest = GrpcUploadBinaryFileRequestTestFactory.createMetadataRequest();
-    private final ChunkedFileSender<GrpcUploadBinaryFileRequest> streamer = new ChunkedFileSender<>(uploadStream, chunkSize, buildUploadBinaryFileRequest,
-            metadataRequest);
-
-    @Nested
-    class TestSendFileContent {
-
-        @Test
-        void shouldNotSendWhenDone() {
-            ReflectionTestUtils.setField(streamer, "hasUploadFile", new AtomicBoolean(false));
-
-            streamer.sendChunkTo(requestObserver);
-
-            verify(requestObserver, never()).onNext(any());
-        }
-
-        @SneakyThrows
-        @Test
-        void shouldCloseUploadStream() {
-            when(uploadStream.readNBytes(anyInt())).thenReturn(new byte[]{});
-
-            streamer.sendChunkTo(requestObserver);
-
-            verify(uploadStream).close();
-        }
-
-        @SneakyThrows
-        @Test
-        void shouldCallOnCompleted() {
-            when(uploadStream.readNBytes(anyInt())).thenReturn(new byte[]{});
-
-            streamer.sendChunkTo(requestObserver);
-
-            verify(requestObserver).onCompleted();
-        }
-
-        @SneakyThrows
-        @Test
-        void shouldCallOnNext() {
-            when(uploadStream.readNBytes(anyInt())).thenReturn(OzgFileTestFactory.DATA);
-
-            streamer.sendChunkTo(requestObserver);
-
-            verify(requestObserver).onNext(uploadBinaryFileRequest);
-        }
-
-        @SneakyThrows
-        @Test
-        void shouldThrowException() {
-            doThrow(IOException.class).when(uploadStream).readNBytes(anyInt());
-
-            assertThrows(RuntimeException.class, () -> streamer.sendChunkTo(requestObserver));
-        }
-
-        @SneakyThrows
-        @Test
-        void shouldBuildRequest() {
-            when(uploadStream.readNBytes(anyInt())).thenReturn(OzgFileTestFactory.DATA);
-
-            streamer.sendChunkTo(requestObserver);
-
-            verify(buildUploadBinaryFileRequest).apply(OzgFileTestFactory.DATA);
-        }
-
-        @SneakyThrows
-        @Test
-        void shouldSendMetadata() {
-            when(uploadStream.readNBytes(anyInt())).thenReturn(OzgFileTestFactory.DATA);
-
-            streamer.sendChunkTo(requestObserver);
-
-            verify(requestObserver).onNext(metadataRequest);
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerTest.java
deleted file mode 100644
index 1ff0d0a54168fa72219ec81ce8e3dc6df645f9ab..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEventService;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.HttpStatusCode;
-import org.springframework.http.MediaType;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.util.concurrent.CompletableFuture;
-
-import static de.mgm.bup.ozg.antragsraum.attachments.OzgFileTestFactory.*;
-import static de.mgm.bup.ozg.antragsraum.nachricht.NachrichtTestFactory.*;
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class FileControllerTest {
-    private static final String CHANNEL_ADDRESS = "http://localhost:9090";
-    @Spy
-    @InjectMocks
-    private FileController fileController;
-
-    @Mock
-    private FileService fileService;
-    @Mock
-    private NachrichtEventService nachrichtEventService;
-
-    @Nested
-    class TestGetFileContent {
-        @BeforeEach
-        void init() {
-            when(fileService.getFile(anyString(), anyString())).thenReturn(OzgFileTestFactory.create());
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(CHANNEL_ADDRESS);
-        }
-
-        @Test
-        void shouldCallGetFile() {
-            fileController.getFileData(FILE_ID, CHANNEL_ADDRESS);
-
-            verify(fileService).getFile(anyString(), anyString());
-        }
-
-        @Test
-        void shouldHaveStatusOk() {
-            var response = fileController.getFileData(FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(response.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(HttpStatus.OK.value()));
-        }
-
-        @Test
-        void shouldHaveContentLength() {
-            var response = fileController.getFileData(FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(response.getHeaders().getContentLength()).isEqualTo(FILE_SIZE);
-        }
-
-        @Test
-        void shouldHaveContentType() {
-            var response = fileController.getFileData(FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.parseMediaType(CONTENT_TYPE));
-        }
-
-        @Test
-        void shouldHaveContentDispositionHeader() {
-            var contentDisposition = fileController.getFileData(FILE_ID, CHANNEL_ADDRESS).getHeaders().getContentDisposition();
-
-            assertThat(contentDisposition.toString()).isEqualTo("attachment; filename=\"" + FILE_NAME + "\"");
-        }
-    }
-
-    @Nested
-    class TestGetFile {
-        @BeforeEach
-        void init() {
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(CHANNEL_ADDRESS);
-        }
-
-        @Test
-        void shouldCallService() {
-            fileController.getFile(FILE_ID, REPLY_TO_NACHRICHT_ID);
-
-            verify(fileService).getFile(any(), anyString());
-        }
-    }
-
-    @Nested
-    class TestFileUpload {
-        private final CompletableFuture<String> fileIdFuture = CompletableFuture.completedFuture(FILE_ID);
-
-        @Mock
-        private MultipartFile multipartFile;
-
-        @BeforeEach
-        void init() {
-            when(fileService.upload(anyString(), anyString(), any())).thenReturn(fileIdFuture);
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(CHANNEL_ADDRESS);
-        }
-
-        @Test
-        void shouldCallService() {
-            fileController.uploadFile(OzgFileTestFactory.VORGANG_ID, REPLY_TO_NACHRICHT_ID, multipartFile);
-
-            verify(fileService).upload(anyString(), anyString(), any(MultipartFile.class));
-        }
-    }
-
-    @Nested
-    class TestFileUploadExceptions {
-
-        @Mock
-        private MultipartFile multipartFile;
-
-        @Test
-        void shouldThrowExceptionWhenVorgangIdIsNull() {
-            assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> fileController.uploadFile(null, REPLY_TO_NACHRICHT_ID, multipartFile));
-        }
-
-        @Test
-        void shouldThrowExceptionWhenRueckfrageIdIsNull() {
-            assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> fileController.uploadFile(OzgFileTestFactory.VORGANG_ID, null, multipartFile));
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileDownloadStreamObserverTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileDownloadStreamObserverTest.java
deleted file mode 100644
index 84f35380eff6f68f25dc16dd4dc214f7455b2cfa..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileDownloadStreamObserverTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import com.google.protobuf.ByteString;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcGetBinaryFileDataResponse;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.concurrent.CompletableFuture;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class FileDownloadStreamObserverTest {
-    @InjectMocks
-    private FileDownloadStreamObserver downloadStreamObserver;
-    @Mock
-    private CompletableFuture<Boolean> streamFuture;
-    @Mock
-    private OutputStream out;
-
-    @Nested
-    class TestOnNext {
-
-        @Test
-        void shouldWriteToStreamOnReceivingContentPart() throws IOException {
-            downloadStreamObserver.onNext(createFileContentResponse());
-
-            verify(out).write(OzgFileTestFactory.DATA);
-        }
-
-        private GrpcGetBinaryFileDataResponse createFileContentResponse() {
-            return GrpcGetBinaryFileDataResponse.newBuilder().setFileContent(ByteString.copyFrom(OzgFileTestFactory.DATA)).build();
-        }
-
-        @Test
-        void shouldHandleException() throws IOException {
-            doThrow(IOException.class).when(out).write(any());
-
-            assertThatExceptionOfType(RuntimeException.class).isThrownBy(
-                    () -> downloadStreamObserver.onNext(createFileContentResponse())).withMessage("Download file error writing on output stream");
-        }
-    }
-
-    @Nested
-    class TestOnError {
-
-        @Mock
-        private Throwable throwable;
-
-        @Test
-        void shouldCompleteFutureExceptionally() {
-            downloadStreamObserver.onError(throwable);
-
-            verify(streamFuture).completeExceptionally(throwable);
-        }
-    }
-
-    @Nested
-    class TestOnCompleted {
-
-        @Test
-        void shouldCompleteFuture() {
-            downloadStreamObserver.onCompleted();
-
-            verify(streamFuture).complete(true);
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcClientTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcClientTest.java
deleted file mode 100644
index 13c6043c4810e9f88c919989dd8bad3c23b8c643..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcClientTest.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import com.google.common.collect.ImmutableList;
-import com.google.protobuf.ByteString;
-import de.itvsh.ozg.pluto.grpc.binaryFile.BinaryFileServiceGrpc;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFilesRequest;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext;
-import de.mgm.bup.ozg.antragsraum.callcontext.ContextService;
-import de.mgm.bup.ozg.antragsraum.common.ChannelPropertiesFactory;
-import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
-import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class FileGrpcClientTest {
-    private final static String CHANNEL_ADDRESS = "static://localhost:9090";
-
-    @Spy
-    @InjectMocks
-    private FileGrpcClient fileGrpcClient;
-
-    @Mock
-    private GlobalClientInterceptorRegistry registry;
-
-    @Mock
-    private ChannelPropertiesFactory channelPropertiesFactory;
-
-    @Mock
-    private ContextService contextService;
-
-    @Nested
-    class TestCreatingChannel {
-        @BeforeEach
-        void init() {
-            when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(new GrpcChannelsProperties());
-        }
-
-        @Test
-        void shouldGetChannelFactory() {
-            var channelFactory = fileGrpcClient.getChannelFactory(CHANNEL_ADDRESS);
-            assertThat(channelFactory).isNotNull();
-        }
-
-        @Test
-        void shouldGetChannel() {
-            when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
-
-            var channel = fileGrpcClient.getChannelFactory(CHANNEL_ADDRESS).createChannel(CHANNEL_ADDRESS);
-
-            assertThat(channel).isNotNull();
-        }
-    }
-
-    @Nested
-    class TestSendingFiles {
-        @Mock
-        private BinaryFileServiceGrpc.BinaryFileServiceStub asyncServiceStub;
-
-        @Mock
-        private FileUploadStreamObserver fileUploadStreamObserver;
-
-        private final UploadOzgFile file = UploadOzgFileTestFactory.createUploadFile(new ByteArrayInputStream(OzgFileTestFactory.DATA));
-
-        @Nested
-        class TestUploadingFile {
-            @BeforeEach
-            void init() {
-                when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
-                when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(new GrpcChannelsProperties());
-                when(contextService.createCallContext()).thenReturn(GrpcCallContext.getDefaultInstance());
-            }
-
-            @Test
-            void shouldCallGetChannel() {
-                fileGrpcClient.uploadFile(file, CHANNEL_ADDRESS, new CompletableFuture<>());
-
-                verify(fileGrpcClient).getChannelFactory(anyString());
-            }
-
-            @Test
-            void shouldCallCreateUploadFileObserver() {
-                fileGrpcClient.uploadFile(file, CHANNEL_ADDRESS, new CompletableFuture<>());
-
-                verify(fileGrpcClient).createUploadFileObserver(any(), any(UploadOzgFile.class));
-            }
-
-            @Test
-            void shouldCallDoUploadFiles() {
-                fileGrpcClient.uploadFile(file, CHANNEL_ADDRESS, new CompletableFuture<>());
-
-                verify(fileGrpcClient).doUploadFiles(any(FileUploadStreamObserver.class), any(BinaryFileServiceGrpc.BinaryFileServiceStub.class));
-            }
-        }
-
-
-        @Test
-        void shouldCreateUploadFileObserver() {
-            when(contextService.createCallContext()).thenReturn(GrpcCallContext.getDefaultInstance());
-
-            var observer = fileGrpcClient.createUploadFileObserver(CompletableFuture.completedFuture(OzgFileTestFactory.FILE_ID), UploadOzgFileTestFactory.create());
-
-            assertThat(observer).isNotNull();
-        }
-
-        @Test
-        void shouldCallUploadBinaryFileAsStream() {
-            fileGrpcClient.doUploadFiles(fileUploadStreamObserver, asyncServiceStub);
-
-            verify(asyncServiceStub).uploadBinaryFileAsStream(any(FileUploadStreamObserver.class));
-        }
-    }
-
-    @Nested
-    class TestBuildChunkRequest {
-        @Test
-        void shouldContainContent() {
-            var chunkRequest = fileGrpcClient.buildChunkRequest(OzgFileTestFactory.DATA);
-
-            assertThat(chunkRequest.getFileContent()).isEqualTo(ByteString.copyFrom(OzgFileTestFactory.DATA));
-        }
-    }
-
-    @Nested
-    class TestLoadingFiles {
-        @Mock
-        private BinaryFileServiceGrpc.BinaryFileServiceBlockingStub serviceStub;
-
-        @Mock
-        private GrpcBinaryFilesRequest request;
-
-        @Test
-        void shouldGetFileMetaData() {
-            fileGrpcClient.getBinaryFilesMetaData(request, serviceStub);
-
-            verify(serviceStub).findBinaryFilesMetaData(any(GrpcBinaryFilesRequest.class));
-        }
-    }
-
-    @Nested
-    class TestDownloadingFiles {
-        @Mock
-        private FileDownloadStreamObserver responseObserver;
-
-        private final ByteArrayOutputStream output = new ByteArrayOutputStream();
-
-        @BeforeEach
-        void init() {
-            doReturn(responseObserver).when(fileGrpcClient).createDownloadFileObserver(any(), any());
-            doNothing().when(fileGrpcClient).waitUntilFutureToComplete(any());
-            when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
-            when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(new GrpcChannelsProperties());
-        }
-
-        @Test
-        void shouldCallGetBinaryFileContent() {
-            fileGrpcClient.downloadFileContent(OzgFileTestFactory.FILE_ID, output, CHANNEL_ADDRESS);
-
-            verify(fileGrpcClient).doDownloadFileContent(anyString(), any(OutputStream.class), any(BinaryFileServiceGrpc.BinaryFileServiceStub.class));
-        }
-
-        @Test
-        void shouldCallCreateDownloadFileObserver() {
-            fileGrpcClient.downloadFileContent(OzgFileTestFactory.FILE_ID, output, CHANNEL_ADDRESS);
-
-            verify(fileGrpcClient).createDownloadFileObserver(any(), any(OutputStream.class));
-        }
-    }
-
-    @Nested
-    class TestWaitUntilFutureToComplete {
-        @Mock
-        private CompletableFuture<Boolean> streamFuture;
-
-        @Test
-        void shouldNotThrowException() {
-            assertDoesNotThrow(() -> fileGrpcClient.waitUntilFutureToComplete(streamFuture));
-        }
-
-        @ParameterizedTest
-        @ValueSource(classes = {InterruptedException.class, ExecutionException.class, TimeoutException.class})
-        void shouldRethrowAsRuntimeException(Class<Exception> exception)
-                throws InterruptedException, ExecutionException, TimeoutException {
-            doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class));
-
-            assertThrows(RuntimeException.class, () -> fileGrpcClient.waitUntilFutureToComplete(streamFuture));
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcTestConfiguration.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcTestConfiguration.java
deleted file mode 100644
index 99a2406e780071cf6a20735563002d491903a089..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileGrpcTestConfiguration.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import com.google.common.collect.ImmutableList;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext;
-import de.mgm.bup.ozg.antragsraum.callcontext.ContextService;
-import de.mgm.bup.ozg.antragsraum.common.ChannelPropertiesFactory;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEventService;
-import lombok.NonNull;
-import net.devh.boot.grpc.client.autoconfigure.*;
-import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
-import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
-import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
-import net.devh.boot.grpc.common.autoconfigure.GrpcCommonTraceAutoConfiguration;
-import net.devh.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration;
-import net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import static org.mockito.Mockito.*;
-
-@Configuration
-@ImportAutoConfiguration({
-        GrpcCommonCodecAutoConfiguration.class,
-        GrpcCommonTraceAutoConfiguration.class,
-
-        GrpcServerAutoConfiguration.class,
-        GrpcServerFactoryAutoConfiguration.class,
-
-        GrpcClientAutoConfiguration.class,
-        GrpcClientHealthAutoConfiguration.class,
-        GrpcClientMetricAutoConfiguration.class,
-        GrpcClientSecurityAutoConfiguration.class,
-        GrpcClientTraceAutoConfiguration.class,
-        GrpcDiscoveryClientAutoConfiguration.class
-})
-public class FileGrpcTestConfiguration {
-    @Bean
-    GrpcBinaryFileServiceStub grpcBinaryFileServiceStub() {
-        return new GrpcBinaryFileServiceStub();
-    }
-
-    @Bean
-    NachrichtEventService nachrichtEventService() {
-        return mock(NachrichtEventService.class);
-    }
-
-    @Bean
-    ContextService contextService() {
-        ContextService contextService = mock(ContextService.class);
-        when(contextService.createCallContext()).thenReturn(GrpcCallContext.getDefaultInstance());
-        return contextService;
-    }
-
-    @Bean
-    FileRemoteService fileRemoteService() {
-        return new FileRemoteService(new FileGrpcClient(initGlobalClientInterceptorRegistry(), initChannelsPropertiesFactory(), contextService()));
-    }
-
-    @NonNull
-    private static GlobalClientInterceptorRegistry initGlobalClientInterceptorRegistry() {
-        GlobalClientInterceptorRegistry globalClientInterceptorRegistry = mock(GlobalClientInterceptorRegistry.class);
-        when(globalClientInterceptorRegistry.getClientInterceptors()).thenReturn(ImmutableList.of());
-
-        return globalClientInterceptorRegistry;
-    }
-
-    @NonNull
-    private static ChannelPropertiesFactory initChannelsPropertiesFactory() {
-        return new ChannelPropertiesFactory(new GrpcChannelsProperties());
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteServiceITCase.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteServiceITCase.java
deleted file mode 100644
index a6c63931211a63dd03ac1b1a889437ee54b43529..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteServiceITCase.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.SpyBean;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import static org.assertj.core.api.Assertions.*;
-
-@SpringBootTest(properties = {
-        "grpc.server.inProcessName=test3",
-        "grpc.server.port=-1"
-})
-@SpringJUnitConfig(classes = {FileGrpcTestConfiguration.class})
-@DirtiesContext
-public class FileRemoteServiceITCase {
-    private static final String CHANNEL_ADDRESS = "in-process:test3";
-
-    @SpyBean
-    private FileRemoteService fileRemoteService;
-
-    @Nested
-    class TestFileUpload {
-
-        @Test
-        @DirtiesContext
-        void shouldUploadFile() {
-            var inputStream = new ByteArrayInputStream(OzgFileTestFactory.DATA);
-
-            var idFuture = fileRemoteService.uploadFile(UploadOzgFileTestFactory.createUploadFile(inputStream), CHANNEL_ADDRESS);
-
-            try {
-                assertThat(idFuture.get(1, TimeUnit.SECONDS)).isEqualTo(OzgFileTestFactory.FILE_ID);
-            } catch (InterruptedException | ExecutionException | TimeoutException e) {
-                fail(e.getMessage());
-            }
-        }
-    }
-
-    @Nested
-    class TestGetFile {
-        @Test
-        @DirtiesContext
-        void shouldGetFile() {
-            var file = fileRemoteService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(file).isNotNull();
-        }
-
-        @Test
-        @DirtiesContext
-        void shouldHaveFileMetaData() {
-            var file = fileRemoteService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(file).isEqualTo(OzgFileTestFactory.create());
-        }
-    }
-
-    @Nested
-    class TestGetFileContent {
-        @Test
-        @DirtiesContext
-        void shouldGetFileContent() {
-            var out = new ByteArrayOutputStream();
-            fileRemoteService.downloadFileContent(OzgFileTestFactory.FILE_ID, out, CHANNEL_ADDRESS);
-
-            assertThat(out.size()).isEqualTo(OzgFileTestFactory.DATA.length);
-        }
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteServiceTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteServiceTest.java
deleted file mode 100644
index bc983cf3cfa4b4acd8054d33bb6318c41b48e141..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileRemoteServiceTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcFindFilesResponse;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class FileRemoteServiceTest {
-    private static final String CHANNEL_ADDRESS = "static://localhost:9090";
-    @Spy
-    @InjectMocks
-    private FileRemoteService remoteService;
-
-    @Mock
-    private FileGrpcClient grpcClient;
-
-    @Nested
-    class TestLoadingFile {
-        @BeforeEach
-        void init() {
-            when(grpcClient.findBinaryFilesMetaData(any(), anyString())).thenReturn(GrpcFindFilesResponse.newBuilder().addFile(OzgFileTestFactory.createGrpcFile()).build());
-        }
-
-        @Test
-        void shouldLoadFileMetadata() {
-            var file = remoteService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(file).isNotNull();
-        }
-
-        @Test
-        void shouldHaveFileName() {
-            var file = remoteService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(file.fileName()).isEqualTo(OzgFileTestFactory.FILE_NAME);
-        }
-
-        @Test
-        void shouldHaveFileSize() {
-            var file = remoteService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(file.fileSize()).isEqualTo(OzgFileTestFactory.FILE_SIZE);
-        }
-
-        @Test
-        void shouldHaveContentType() {
-            var file = remoteService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(file.contentType()).isEqualTo(OzgFileTestFactory.CONTENT_TYPE);
-        }
-
-        @Test
-        void shouldHaveFileId() {
-            var file = remoteService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
-
-            assertThat(file.id()).isEqualTo(OzgFileTestFactory.FILE_ID);
-        }
-    }
-
-    @Nested
-    class TestDownloadFileContent {
-        private final ByteArrayOutputStream output = new ByteArrayOutputStream();
-
-        @Test
-        void shouldCallDownloadFileObserver() {
-            remoteService.downloadFileContent(OzgFileTestFactory.FILE_ID, output, CHANNEL_ADDRESS);
-
-            verify(grpcClient).downloadFileContent(anyString(), any(OutputStream.class), anyString());
-        }
-    }
-
-
-    @Nested
-    class TestUploadFile {
-        private final InputStream input = new ByteArrayInputStream(OzgFileTestFactory.DATA);
-
-        @Test
-        void shouldCallGrpcClient() {
-
-            remoteService.uploadFile(UploadOzgFileTestFactory.createUploadFile(input), CHANNEL_ADDRESS);
-
-            verify(grpcClient).uploadFile(any(UploadOzgFile.class), anyString(), any());
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileUploadStreamObserverTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileUploadStreamObserverTest.java
deleted file mode 100644
index 06350ce987f5f5e904ece416bb89b6182ff084dc..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileUploadStreamObserverTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileResponse;
-import io.grpc.ManagedChannel;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.test.util.ReflectionTestUtils;
-
-import java.util.concurrent.CompletableFuture;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class FileUploadStreamObserverTest {
-    @InjectMocks
-    private FileUploadStreamObserver uploadStreamObserver;
-    @Mock
-    private CompletableFuture<String> fileIdFuture;
-
-    @Mock
-    private ManagedChannel channel;
-
-    @Nested
-    class TestOnNext {
-
-        private final GrpcUploadBinaryFileResponse uploadResponse = GrpcUploadBinaryFileResponse.newBuilder()
-                .setFileId(OzgFileTestFactory.FILE_ID)
-                .build();
-
-        @Test
-        void shouldSetFileId() {
-            uploadStreamObserver.onNext(uploadResponse);
-
-            assertThat(uploadStreamObserver.getFileId()).isEqualTo(OzgFileTestFactory.FILE_ID);
-        }
-    }
-
-    @Nested
-    class TestOnError {
-
-        @Mock
-        private Throwable throwable;
-
-        @Test
-        void shouldCompleteFutureExceptionally() {
-            uploadStreamObserver.onError(throwable);
-
-            verify(fileIdFuture).completeExceptionally(throwable);
-        }
-    }
-
-    @Nested
-    class TestOnCompleted {
-
-        @Test
-        void shouldCompleteWithFileId() {
-            String FIELD_FILE_ID = "fileId";
-            ReflectionTestUtils.setField(uploadStreamObserver, FIELD_FILE_ID, OzgFileTestFactory.FILE_ID);
-
-            uploadStreamObserver.onCompleted();
-
-            verify(fileIdFuture).complete(OzgFileTestFactory.FILE_ID);
-        }
-
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcBinaryFileServiceStub.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcBinaryFileServiceStub.java
deleted file mode 100644
index 5685b653f960cca86f88e9bbb39f9a03478fe662..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcBinaryFileServiceStub.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import com.google.protobuf.ByteString;
-import de.itvsh.ozg.pluto.grpc.binaryFile.*;
-import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile;
-import io.grpc.stub.StreamObserver;
-import net.devh.boot.grpc.server.service.GrpcService;
-
-@GrpcService
-public class GrpcBinaryFileServiceStub extends BinaryFileServiceGrpc.BinaryFileServiceImplBase {
-
-    public StreamObserver<GrpcUploadBinaryFileRequest> uploadBinaryFileAsStream(StreamObserver<GrpcUploadBinaryFileResponse> responseObserver) {
-        return new UploadStreamObserverStub(responseObserver);
-    }
-
-    public void getBinaryFileContent(GrpcGetBinaryFileDataRequest request, StreamObserver<GrpcGetBinaryFileDataResponse> responseObserver) {
-        responseObserver.onNext(GrpcGetBinaryFileDataResponse.newBuilder().setFileContent(ByteString.copyFrom(OzgFileTestFactory.DATA)).build());
-        responseObserver.onCompleted();
-    }
-
-    public void findBinaryFilesMetaData(GrpcBinaryFilesRequest request, StreamObserver<GrpcFindFilesResponse> responseObserver) {
-        var file = GrpcOzgFile.newBuilder()
-                .setContentType(OzgFileTestFactory.CONTENT_TYPE)
-                .setSize(OzgFileTestFactory.FILE_SIZE)
-                .setId(OzgFileTestFactory.FILE_ID)
-                .setName(OzgFileTestFactory.FILE_NAME)
-                .build();
-
-        responseObserver.onNext(GrpcFindFilesResponse.newBuilder().addFile(file).build());
-        responseObserver.onCompleted();
-
-    }
-
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcUploadBinaryFileRequestTestFactory.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcUploadBinaryFileRequestTestFactory.java
deleted file mode 100644
index 84b6f5880c8583cc10bf6b3add3842a3763a22d0..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcUploadBinaryFileRequestTestFactory.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import com.google.protobuf.ByteString;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest;
-
-class GrpcUploadBinaryFileRequestTestFactory {
-    public static GrpcUploadBinaryFileRequest createDataRequest() {
-        return createBuilder().setFileContent(ByteString.copyFrom(OzgFileTestFactory.DATA)).build();
-    }
-
-    public static GrpcUploadBinaryFileRequest createMetadataRequest() {
-        return createBuilder().setMetadata(GrpcUploadBinaryFileMetaDataTestFactory.create()).build();
-    }
-
-    public static GrpcUploadBinaryFileRequest.Builder createBuilder() {
-        return GrpcUploadBinaryFileRequest.newBuilder();
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileTestFactory.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileTestFactory.java
deleted file mode 100644
index 8804a6e9780175ab066a337b3f27ff1882c00623..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileTestFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile;
-import org.apache.http.entity.ContentType;
-import org.springframework.mock.web.MockMultipartFile;
-
-import java.util.UUID;
-
-class OzgFileTestFactory {
-    static final String NACHRICHT_ID = UUID.randomUUID().toString();
-    static final String VORGANG_ID = UUID.randomUUID().toString();
-    static final String CONTENT_TYPE = ContentType.APPLICATION_OCTET_STREAM.toString();
-    static final long FILE_SIZE = 10;
-    static final String FILE_NAME = "Testfile.zip";
-    static final String FILE_ID = UUID.randomUUID().toString();
-    public static final byte[] DATA = "juhu, ein bild".getBytes();
-    public static final MockMultipartFile TEST_FILE = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA);
-
-    static OzgFile create() {
-        return createBuilder().build();
-    }
-
-    static OzgFile.OzgFileBuilder createBuilder() {
-        return OzgFile.builder()
-                .id(FILE_ID)
-                .contentType(CONTENT_TYPE)
-                .fileSize(FILE_SIZE)
-                .fileName(FILE_NAME);
-    }
-
-    static GrpcOzgFile createGrpcFile() {
-        return createGrpcFileBuilder().build();
-    }
-
-    static GrpcOzgFile.Builder createGrpcFileBuilder() {
-        return GrpcOzgFile.newBuilder()
-                .setId(FILE_ID)
-                .setContentType(CONTENT_TYPE)
-                .setSize(FILE_SIZE)
-                .setName(FILE_NAME);
-    }
-
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadFileSizeValidatorTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadFileSizeValidatorTest.java
deleted file mode 100644
index ffc528604296c8a481cc77f101c744fe58227af2..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadFileSizeValidatorTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import jakarta.validation.ConstraintValidatorContext;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.util.unit.DataSize;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-class UploadFileSizeValidatorTest {
-    private static final UploadOzgFile validFile = UploadOzgFileTestFactory.createUploadFile(null);
-    private static final UploadOzgFile tooLargeFile = UploadOzgFileTestFactory.createBuilder()
-            .fileData(OzgFileTestFactory.createBuilder().fileSize(150_000_000L).build()).build();
-
-    @Nested
-    class TestDefaults {
-        private final FileProperties fileProperties = new FileProperties();
-        private final UploadFileSizeValidator validator = new UploadFileSizeValidator(fileProperties);
-
-        @Test
-        void shouldBeValidDefault() {
-            var res = validator.isValid(validFile, mock(ConstraintValidatorContext.class));
-
-            assertThat(res).isTrue();
-        }
-
-        @Test
-        void shouldNotBeValid() {
-            var res = validator.isValid(tooLargeFile, mock(ConstraintValidatorContext.class));
-
-            assertThat(res).isFalse();
-        }
-    }
-
-    @Nested
-    class TestWithFileProperties {
-        private final FileProperties fileProperties = new FileProperties();
-        UploadFileSizeValidator nonDefaultValidator;
-
-        @BeforeEach
-        void init() {
-            fileProperties.setMaxFileSize(DataSize.ofMegabytes(50));
-            nonDefaultValidator = new UploadFileSizeValidator(fileProperties);
-        }
-
-        @Test
-        void shouldBeValidProperties() {
-            var res = nonDefaultValidator.isValid(validFile, mock(ConstraintValidatorContext.class));
-
-            assertThat(res).isTrue();
-        }
-
-        @Test
-        void shouldNotBeValidProperties() {
-            var res = nonDefaultValidator.isValid(tooLargeFile, mock(ConstraintValidatorContext.class));
-
-            assertThat(res).isFalse();
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadOzgFileTestFactory.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadOzgFileTestFactory.java
deleted file mode 100644
index 8f3d265e9d2b21473ae6848525938530c632fba1..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadOzgFileTestFactory.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import org.apache.http.entity.ContentType;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.UUID;
-
-class UploadOzgFileTestFactory {
-    static final String FILE_ID = UUID.randomUUID().toString();
-    static final String REPLAY_TO_NACHRICHT_ID = UUID.randomUUID().toString();
-    static final String VORGANG_ID = UUID.randomUUID().toString();
-    static final String CONTENT_TYPE = ContentType.APPLICATION_OCTET_STREAM.toString();
-    static final long FILE_SIZE = 10;
-    static final String FILE_NAME = "Testfile.txt";
-    public static final byte[] DATA = "juhu, ein bild".getBytes();
-
-    static UploadOzgFile create() {
-        return createUploadFile(new ByteArrayInputStream(DATA));
-    }
-
-    static UploadOzgFile createUploadFile(InputStream inputStream) {
-        return createBuilder()
-                .fileContent(inputStream)
-                .build();
-    }
-
-    static UploadOzgFile.UploadOzgFileBuilder createBuilder() {
-        return createBuilderOfType(FILE_NAME);
-    }
-
-    static UploadOzgFile.UploadOzgFileBuilder createBuilderOfType(String fileName) {
-        return new UploadOzgFile.UploadOzgFileBuilder()
-                .vorgangId(VORGANG_ID)
-                .fileData(OzgFile.builder()
-                        .id(FILE_ID)
-                        .contentType(CONTENT_TYPE)
-                        .fileSize(FILE_SIZE)
-                        .fileName(fileName)
-                        .build());
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadStreamObserverStub.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadStreamObserverStub.java
deleted file mode 100644
index ddeb78b8b2dde1ab563a67b84495b5a9480e32f3..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/UploadStreamObserverStub.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileResponse;
-import io.grpc.stub.StreamObserver;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.extern.log4j.Log4j2;
-
-import java.io.IOException;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@Log4j2
-public class UploadStreamObserverStub implements StreamObserver<GrpcUploadBinaryFileRequest> {
-    public static final int CHUNK_SIZE = 255 * 1024;
-    private final StreamObserver<GrpcUploadBinaryFileResponse> responseObserver;
-    @Getter(value = AccessLevel.PACKAGE)
-    private PipedOutputStream pipedOutput;
-    @Getter(value = AccessLevel.PACKAGE)
-    private PipedInputStream pipedInput;
-
-    private CompletableFuture<String> fileIdFuture;
-
-    public UploadStreamObserverStub(StreamObserver<GrpcUploadBinaryFileResponse> responseObserver) {
-        this.responseObserver = responseObserver;
-    }
-
-    @Override
-    public void onNext(GrpcUploadBinaryFileRequest fileUploadRequest) {
-        if (fileUploadRequest.hasMetadata()) {
-            initPipedStreams();
-            fileIdFuture = storeFileMetaData(fileUploadRequest, pipedInput, pipedOutput);
-        } else {
-            storeFileContent(fileUploadRequest, pipedOutput);
-        }
-    }
-
-    void initPipedStreams() {
-        pipedOutput = new PipedOutputStream();
-        pipedInput = new PipedInputStream(CHUNK_SIZE);
-    }
-
-    void storeFileContent(GrpcUploadBinaryFileRequest fileUploadRequest, PipedOutputStream pipedOutput) {
-        try {
-            pipedOutput.write(fileUploadRequest.getFileContent().toByteArray());
-        } catch (IOException e) {
-            LOG.error("Fehler beim Speichern des Dateiinhalts.", e);
-        }
-    }
-
-    CompletableFuture<String> storeFileMetaData(GrpcUploadBinaryFileRequest fileUploadRequest, PipedInputStream pipedInput,
-                                                PipedOutputStream pipedOutput) {
-        connectInputStreamToOutputStream(pipedInput, pipedOutput);
-
-        return storeMetaData(fileUploadRequest);
-    }
-
-    void connectInputStreamToOutputStream(PipedInputStream is, PipedOutputStream os) {
-        try {
-            is.connect(os);
-        } catch (IOException e) {
-            LOG.warn("Fehler beim Verbinden der Input/Outputs", e);
-        }
-    }
-
-    CompletableFuture<String> storeMetaData(GrpcUploadBinaryFileRequest fileUploadRequest) {
-        LOG.info("Received request: " + fileUploadRequest.toString());
-        return CompletableFuture.completedFuture(OzgFileTestFactory.FILE_ID);
-    }
-
-    OzgFile buildOzgFile(GrpcUploadBinaryFileRequest fileUploadRequest) {
-        return OzgFile.builder()
-                .fileName(fileUploadRequest.getMetadata().getFileName())
-                .fileSize(fileUploadRequest.getMetadata().getSize())
-                .contentType(fileUploadRequest.getMetadata().getContentType())
-                .build();
-    }
-
-    @Override
-    public void onError(Throwable t) {
-        LOG.error("GrpcLargeFileRequestObserver onError", t);
-        throw new RuntimeException("GrpcLargeFileRequestObserver onError has been called", t);
-    }
-
-    @Override
-    public void onCompleted() {
-        try {
-            pipedOutput.close();
-
-            responseObserver.onNext(GrpcUploadBinaryFileResponse.newBuilder().setFileId(fileIdFuture.get(3, TimeUnit.SECONDS)).build());
-
-            pipedInput.close();
-        } catch (ExecutionException | IOException | TimeoutException e) {
-            handleException(e);
-        } catch (InterruptedException ie) {
-            handleException(ie);
-            Thread.currentThread().interrupt();
-        }
-        responseObserver.onCompleted();
-
-    }
-
-    void handleException(Exception e) {
-        responseObserver.onNext(GrpcUploadBinaryFileResponse.newBuilder().build());
-        throw new RuntimeException(e.getMessage(), e);
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/VirusScannerClientTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/VirusScannerClientTest.java
deleted file mode 100755
index afd2bc3c07531a630016ae73b388d6f7c1188b8b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/VirusScannerClientTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.attachments;
-
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-import xyz.capybara.clamav.ClamavClient;
-import xyz.capybara.clamav.ClamavException;
-import xyz.capybara.clamav.commands.scan.result.ScanResult;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-@ExtendWith(MockitoExtension.class)
-public class VirusScannerClientTest {
-    @Spy
-    @InjectMocks
-    private VirusScannerClient virusScannerClient;
-
-    @Mock
-    private ClamavClient clamavClient;
-
-    @Nested
-    class TestScanningNonNullFile {
-
-        @Test
-        void shouldReturnEmptyMap() {
-            Map<String, Collection<String>> viruses = new HashMap<>();
-            byte[] fileData = new byte[0];
-            when(clamavClient.scan(any(InputStream.class))).thenReturn(any(ScanResult.OK.class));
-
-            assertThat(virusScannerClient.scan(fileData)).isEqualTo(viruses);
-        }
-
-        @Test
-        void shouldThrowTechnicalExceptionOnScan() throws ClamavException {
-            byte[] fileData = new byte[0];
-            doThrow(ClamavException.class).when(clamavClient).scan(any(InputStream.class));
-
-            assertThatExceptionOfType(TechnicalException.class).isThrownBy(
-                    () -> virusScannerClient.scan(fileData));
-        }
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/callcontext/ContextServiceTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/callcontext/ContextServiceTest.java
deleted file mode 100644
index 50a09648f543631a999de54884bdc3dedfaad424..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/callcontext/ContextServiceTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.callcontext;
-
-import de.mgm.bup.ozg.antragsraum.common.AuthenticationHelper;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.oauth2.jwt.Jwt;
-import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
-import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
-import org.springframework.security.test.context.support.WithMockUser;
-
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-
-import static de.mgm.bup.ozg.antragsraum.callcontext.ContextService.*;
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class ContextServiceTest {
-    public static final String ANTRAGSRAUM = "Antragsraum";
-    @Spy
-    @InjectMocks
-    private ContextService contextService;
-
-    @Mock
-    private AuthenticationHelper authenticationHelper;
-
-    @Nested
-    class TestCallContext {
-        @Test
-        void createCallContext() {
-            var grpcContext = contextService.createCallContext();
-
-            assertThat(grpcContext).isNotNull();
-        }
-
-        @Test
-        void getClientName() {
-            var name = contextService.getClientName();
-
-            assertThat(name).isEqualTo(ANTRAGSRAUM);
-        }
-
-        @Test
-        void getRequestId() {
-            var requestId = contextService.getRequestId();
-
-            assertThat(requestId).isNotNull();
-        }
-
-        @Test
-        void getEmptyRequestId() {
-            contextService.requestAttributes = null;
-
-            var requestId = contextService.getRequestId();
-
-            assertThat(requestId).isEqualTo("- no request scope -");
-        }
-
-        @Nested
-        class WithAuthentication {
-
-            @Test
-            void getJwtAuthenticationToken() {
-                setupToken("JWT");
-
-                var token = contextService.getAuthenticationToken();
-
-                assertThat(token).isNotNull();
-            }
-
-            @Test
-            void getBearerAuthenticationToken() {
-                setupToken("BEARER");
-
-                var token = contextService.getAuthenticationToken();
-
-                assertThat(token).isNotNull();
-            }
-
-            @Test
-            void getOtherAuthenticationToken() {
-                setupToken("OTHER");
-
-                var token = contextService.getAuthenticationToken();
-
-                assertThat(token).isEmpty();
-            }
-        }
-    }
-
-    @Nested
-    class TestCallContextMetadata {
-
-        @BeforeEach
-        void init() {
-            setupToken("JWT");
-        }
-
-        @Test
-        void buildCallContextMetadata() {
-            var metaData = contextService.buildCallContextMetadata();
-
-            assertThat(metaData).isNotNull();
-        }
-
-        @Test
-        @WithMockUser
-        void shouldHaveClientName() {
-            var metaData = contextService.buildCallContextMetadata();
-
-            assertThat(metaData.get(ContextService.createKeyOf(KEY_CLIENT_NAME)))
-                    .isEqualTo(CLIENT_NAME.getBytes());
-        }
-
-        @Test
-        void shouldHaveRequestId() {
-            var metaData = contextService.buildCallContextMetadata();
-
-            assertThat(metaData.get(ContextService.createKeyOf(KEY_REQUEST_ID)))
-                    .isNotNull();
-        }
-
-        @Test
-        void shouldHaveAuthToken() {
-            var metaData = contextService.buildCallContextMetadata();
-
-            assertThat(metaData.get(ContextService.createKeyOf(KEY_AUTHENTICATION_ID)))
-                    .isNotNull();
-        }
-    }
-
-    private void setupToken(String tokenType) {
-        switch (tokenType) {
-            case "JWT" -> {
-                JwtAuthenticationToken authentication = mock(JwtAuthenticationToken.class);
-                when(authentication.getToken()).thenReturn(createToken());
-                when(authenticationHelper.getAuthentication()).thenReturn(authentication);
-            }
-            case "BEARER" -> {
-                BearerTokenAuthenticationToken authentication = mock(BearerTokenAuthenticationToken.class);
-                when(authentication.getToken()).thenReturn(createToken().getTokenValue());
-                when(authenticationHelper.getAuthentication()).thenReturn(authentication);
-            }
-            default -> {
-                var authentication = mock(Authentication.class);
-                when(authenticationHelper.getAuthentication()).thenReturn(authentication);
-            }
-        }
-    }
-
-    private Jwt createToken() {
-        String jwtTokenPayload = """
-                  {
-                      "exp": 1699951718,
-                          "iat": 1699951418,
-                          "jti": "3ebe34c0-875b-4253-8428-a1736546b3d0",
-                          "iss": "http://localhost:32804/realms/antragsraum",
-                          "aud": "account",
-                          "sub": "80b69dc1-7d37-43df-9efe-64584f5d25c8",
-                          "typ": "Bearer",
-                          "azp": "antragsraum-api",
-                          "session_state": "35aeb2f0-9f88-46e6-befa-b49773afbf0b",
-                          "allowed-origins": [
-                      "http://localhost:8080",
-                              "/*",
-                              "https://localhost:8080"
-                ],
-                      "resource_access": {
-                      "account": {
-                          "roles": [
-                          "manage-account",
-                                  "manage-account-links",
-                                  "view-profile"
-                    ]
-                      }
-                  },
-                      "scope": "email profile",
-                          "sid": "35aeb2f0-9f88-46e6-befa-b49773afbf0b",
-                          "email_verified": false,
-                          "name": "Jane Doe",
-                          "preferred_username": "janedoe",
-                          "given_name": "Jane",
-                          "family_name": "Doe",
-                          "email": "jane.doe@ozg-cloud.com"
-                  }
-                  """;
-        return Jwt.withTokenValue(jwtTokenPayload)
-                .header("alg", "RS256")
-                .header("typ", "jwt")
-                .header("kid", "NZ4PVRP0uUtxqh7CNBsLUdHSd5rbpGbIG247kepoadc")
-                .claim("iss", "https://localhost/realms/antragsraum")
-                .claim("sub", "281c4558-550c-413b-9972-2d2e5bde6b9b")
-                .claim("iat", Instant.now())
-                .claim("exp", Instant.now().plus(1, ChronoUnit.DAYS))
-                .claim("preferred_username", "janedoe")
-                .build();
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventMapperTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventMapperTest.java
deleted file mode 100644
index e4a89d4029c4be30f0a1983a25a753146a219069..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventMapperTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.events;
-
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.*;
-
-class NachrichtEventMapperTest {
-    @Nested
-    class TestMapFromGrpc {
-        @Test
-        void shouldMap() {
-            var nachrichtEvent = NachrichtEventMapper.fromGrpc(GrpcNachrichtTestFactory.createBuilder().build());
-
-            assertThat(nachrichtEvent).isNotNull();
-        }
-
-        @Test
-        void shouldHaveNachrichtId() {
-            var nachrichtEvent = NachrichtEventMapper.fromGrpc(GrpcNachrichtTestFactory.createBuilder().build());
-
-            assertThat(nachrichtEvent.rueckfrageId()).isEqualTo(GrpcNachrichtTestFactory.NACHRICHTEN_ID);
-        }
-
-        @Test
-        void shouldHavePostfachId() {
-            var nachrichtEvent = NachrichtEventMapper.fromGrpc(GrpcNachrichtTestFactory.createBuilder().build());
-
-            assertThat(nachrichtEvent.postfachId()).isEqualTo(GrpcNachrichtTestFactory.POSTFACH_ID);
-        }
-
-        @Test
-        void shouldHaveVorgangId() {
-            var nachrichtEvent = NachrichtEventMapper.fromGrpc(GrpcNachrichtTestFactory.createBuilder().build());
-
-            assertThat(nachrichtEvent.vorgangId()).isEqualTo(GrpcNachrichtTestFactory.VORGANG_ID);
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteServiceITCase.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteServiceITCase.java
deleted file mode 100644
index 29c7426bba2da43f5189af1acf7d08afb834a954..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteServiceITCase.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.events;
-
-import de.mgm.bup.ozg.antragsraum.nachricht.NachrichtEventTestFactory;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
-
-import static org.assertj.core.api.Assertions.*;
-
-@SpringBootTest(properties = {
-        "grpc.server.inProcessName=test",
-        "grpc.server.port=-1",
-        "grpc.client.inProcess.address=in-process:test"
-})
-@SpringJUnitConfig(classes = {NachrichtEventGrpcTestConfiguration.class})
-@DirtiesContext
-@ExtendWith(MockitoExtension.class)
-class NachrichtEventRemoteServiceITCase {
-
-    @Autowired
-    NachrichtEventRemoteService nachrichtEventRemoteService;
-
-    @Nested
-    class TestInformationAvailable {
-
-        @Test
-        @DirtiesContext
-        void shouldLoadNachrichtEvents() {
-            var result = nachrichtEventRemoteService.getNachrichtEventsOfPostfach(InformationGrpcServiceStub.NACHRICHT_EVENT.postfachId());
-
-            assertThat(result).hasSize(1);
-        }
-
-        @Test
-        @DirtiesContext
-        void shouldLoadUrl() {
-            var result = nachrichtEventRemoteService.getServiceUrl(InformationGrpcServiceStub.NACHRICHT_EVENT.rueckfrageId());
-
-            assertThat(result).hasValue(NachrichtEventTestFactory.URL);
-        }
-    }
-
-    @Nested
-    class TestNoInformationAvailable {
-        @Test
-        void shouldLoadEmptyNachrichtEvents() {
-            var result = nachrichtEventRemoteService.getNachrichtEventsOfPostfach(InformationGrpcServiceStub.EMPTY_POSTFACH_ID);
-
-            assertThat(result).isEmpty();
-        }
-
-        @Test
-        @DirtiesContext
-        void shouldLoadEmptyUrl() {
-            var result = nachrichtEventRemoteService.getServiceUrl(InformationGrpcServiceStub.EMPTY_NACHRICHT_ID);
-
-            assertThat(result).isNotPresent();
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventServiceTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventServiceTest.java
deleted file mode 100644
index d302bc1e4e3cd878c2c40f7ca1fd3b3e8893ace9..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventServiceTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.events;
-
-import de.mgm.bup.ozg.antragsraum.common.AddressNotFoundException;
-import de.mgm.bup.ozg.antragsraum.nachricht.NachrichtTestFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.Optional;
-
-import static org.assertj.core.api.AssertionsForClassTypes.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class NachrichtEventServiceTest {
-    @Spy
-    @InjectMocks
-    private NachrichtEventService service;
-    @Mock
-    private NachrichtEventRemoteService remoteService;
-
-    @Test
-    void shouldCallRemoteServiceForEvents() {
-        service.getNachrichtEventsOfPostfachId(NachrichtTestFactory.POSTFACH_ID);
-
-        verify(remoteService).getNachrichtEventsOfPostfach(anyString());
-    }
-
-    @Test
-    void shouldCallRemoteServiceForUrl() {
-        when(remoteService.getServiceUrl(anyString())).thenReturn(Optional.of("test"));
-
-        service.getServiceUrlOfNachricht(NachrichtTestFactory.REPLY_TO_NACHRICHT_ID);
-
-        verify(remoteService).getServiceUrl(anyString());
-    }
-
-    @Test
-    void shouldThrowAddressNotFoundException() {
-        assertThatExceptionOfType(AddressNotFoundException.class).isThrownBy(
-                () -> service.getServiceUrlOfNachricht(NachrichtTestFactory.REPLY_TO_NACHRICHT_ID));
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/DataMapperTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/DataMapperTest.java
deleted file mode 100644
index cba424d47a6957a04d099c169a4643ff4348a61b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/DataMapperTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrage;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrageAnswer;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import java.util.Date;
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.*;
-
-class DataMapperTest {
-
-    public static final String FILE_NAME = "Testattachment.txt";
-    public static final String CONTENT_TYPE = "text/plain";
-    public static final long SIZE = 42L;
-    public static final String FILE_ID = "file_id";
-    public static final String RUECKFRAGE_ID = "rId";
-    public static final String ANTWORT_TEXT = "Antwort text";
-    public static final String RUECKFRAGE_TEXT = "Rueckfrage Test";
-    public static final String VORGANG_ID = "vorgang_id";
-
-    @Nested
-    class TestFromGrpcRueckfrage {
-        @Test
-        void testFromGrpcRueckfrage() {
-            var res = DataMapper.fromGrpcRueckfrage(create());
-
-            assertThat(res).isNotNull();
-        }
-
-        @Test
-        void testRueckfrageId() {
-            var res = DataMapper.fromGrpcRueckfrage(create());
-
-            assertThat(res.id()).isEqualTo(RUECKFRAGE_ID);
-        }
-
-        @Test
-        void testRueckfrageMessage() {
-            var res = DataMapper.fromGrpcRueckfrage(create());
-
-            assertThat(res.message()).isEqualTo(RUECKFRAGE_TEXT);
-        }
-    }
-
-    @Nested
-    class TestMappingIfReplyNachricht {
-        @Test
-        void testAntwortFromGrpcRueckfrage() {
-            var res = DataMapper.fromGrpcRueckfrage(create());
-
-            assertThat(res.replyNachrichten()).isNotNull();
-        }
-
-        @Test
-        void testAntwortId() {
-            var res = DataMapper.fromGrpcRueckfrage(create()).replyNachrichten().get(0);
-
-            assertThat(res.id()).isEqualTo(RUECKFRAGE_ID);
-        }
-
-        @Test
-        void testAntwortText() {
-            var res = DataMapper.fromGrpcRueckfrage(create()).replyNachrichten().get(0);
-
-            assertThat(res.message()).isEqualTo(ANTWORT_TEXT);
-        }
-    }
-
-    @Nested
-    class TestMappingOfAttachments {
-        @Test
-        void testGetAttachments() {
-            var res = DataMapper.getAttachments(create());
-            assertThat(res).isNotEmpty();
-        }
-
-        @Test
-        void testGetAttachmentsSize() {
-            var res = DataMapper.getAttachments(create());
-            assertThat(res).hasSize(1);
-        }
-
-        @Test
-        void testGetAttachmentName() {
-            var res = DataMapper.getAttachments(create()).get(0);
-            assertThat(res.fileName()).isEqualTo(FILE_NAME);
-        }
-
-        @Test
-        void testGetAttachmentContentType() {
-            var res = DataMapper.getAttachments(create()).get(0);
-            assertThat(res.contentType()).isEqualTo(CONTENT_TYPE);
-        }
-
-        @Test
-        void testGetAttachmentSize() {
-            var res = DataMapper.getAttachments(create()).get(0);
-            assertThat(res.fileSize()).isEqualTo(SIZE);
-        }
-
-        @Test
-        void testGetAttachmentId() {
-            var res = DataMapper.getAttachments(create()).get(0);
-            assertThat(res.id()).isEqualTo(FILE_ID);
-        }
-    }
-
-    @Test
-    void testFromGrpcUploadBinaryFileMetaData() {
-        var res = DataMapper.fromGrpcUploadBinaryFileMetaData(createMetadata(), FILE_ID);
-
-        assertThat(res).isNotNull();
-        assertThat(res.getId()).isEqualTo(FILE_ID);
-        assertThat(res.getContentType()).isEqualTo(CONTENT_TYPE);
-        assertThat(res.getName()).isEqualTo(FILE_NAME);
-        assertThat(res.getSize()).isEqualTo(SIZE);
-    }
-
-    private GrpcRueckfrage create() {
-        return GrpcRueckfrage.newBuilder()
-                .setId(RUECKFRAGE_ID)
-                .setAnsweredAt(new Date().toString())
-                .setSendAt(new Date().toString())
-                .setText(RUECKFRAGE_TEXT)
-                .addAllAttachment(List.of(GrpcBinaryFile.newBuilder()
-                        .setSize(SIZE)
-                        .setName(FILE_NAME)
-                        .setContentType(CONTENT_TYPE)
-                        .setId(FILE_ID)
-                        .build()))
-                .addAllAnswers(List.of(GrpcRueckfrageAnswer.newBuilder()
-                        .setAnswerText(ANTWORT_TEXT)
-                        .setRueckfrageId(RUECKFRAGE_ID)
-                        .addAllAnswerAttachment(List.of(GrpcBinaryFile.newBuilder()
-                                .setSize(SIZE)
-                                .setName(FILE_NAME)
-                                .setContentType(CONTENT_TYPE)
-                                .setId(FILE_ID)
-                                .build()))
-                        .build()))
-                .build();
-    }
-
-    GrpcUploadBinaryFileMetaData createMetadata() {
-        return GrpcUploadBinaryFileMetaData.newBuilder()
-                .setSize(SIZE)
-                .setContentType(CONTENT_TYPE)
-                .setVorgangId(VORGANG_ID)
-                .setFileName(FILE_NAME)
-                .build();
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/StubDataRepositoryTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/StubDataRepositoryTest.java
deleted file mode 100644
index bb581d12b89a5f95cc91d3a54d534c153cec670b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/StubDataRepositoryTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
-import de.mgm.bup.ozg.antragsraum.common.NotFoundException;
-import de.mgm.bup.ozg.antragsraum.nachricht.NachrichtEventTestFactory;
-import org.junit.jupiter.api.Test;
-
-import java.util.UUID;
-
-import static de.mgm.bup.ozg.antragsraum.mocks.GrpcDataCreator.*;
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-class StubDataRepositoryTest {
-    private final WritingNachrichtenEventService nachrichtEventService = mock(WritingNachrichtenEventService.class);
-
-    private final StubDataRepository repo = new StubDataRepository(nachrichtEventService);
-
-    @Test
-    void getRueckfragenResponseWithInit() {
-        var val = repo.getRueckfragenResponse(POSTFACH_ID_1);
-        assertThat(val.getRueckfrageCount()).isEqualTo(1);
-        assertThat(val.getRueckfrage(0)).isNotNull().hasFieldOrPropertyWithValue("vorgangId", VORGANG_ID_1);
-    }
-
-    @Test
-    void getRueckfragenResponseWithoutInit() {
-        var nachrichtId = UUID.randomUUID().toString();
-        var reply = getRueckfrageBuilder(VORGANG_ID_2).setId(nachrichtId).build();
-        var event = NachrichtEventTestFactory.createBuilder().rueckfrageId(reply.getId()).build();
-
-        repo.addReply(reply, event);
-
-        var val = repo.getRueckfragenResponse(event.postfachId());
-        assertThat(val.getRueckfrageCount()).isEqualTo(1);
-        assertThat(val.getRueckfrage(0)).isNotNull().hasFieldOrPropertyWithValue("vorgangId", VORGANG_ID_2);
-    }
-
-    @Test
-    void getRueckfrageResponse() {
-        var val = repo.getRueckfrageResponse(NACHRICHT_ID_1_0);
-
-        assertThat(val.getRueckfrage()).isNotNull().hasFieldOrPropertyWithValue("vorgangId", VORGANG_ID_1);
-    }
-
-    @Test
-    void getRueckfrageResponseWithoutInit() {
-        var nachrichtId = UUID.randomUUID().toString();
-        var reply = getRueckfrageBuilder(VORGANG_ID_2).setId(nachrichtId).build();
-        var event = NachrichtEventTestFactory.createBuilder().rueckfrageId(reply.getId()).build();
-
-        repo.addReply(reply, event);
-
-        var val = repo.getRueckfrageResponse(nachrichtId);
-
-        assertThat(val.getRueckfrage()).isNotNull().hasFieldOrPropertyWithValue("vorgangId", VORGANG_ID_2);
-    }
-
-    @Test
-    void removeNachricht() {
-        var val = repo.getRueckfrageResponse(NACHRICHT_ID_1_0);
-
-        repo.removeNachricht(val.getRueckfrage().getId());
-
-        assertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> repo.getRueckfrageResponse(NACHRICHT_ID_1_0));
-    }
-
-    @Test
-    void getAllAddedNachrichtenIds() {
-        var nachrichtId = UUID.randomUUID().toString();
-        var reply = getRueckfrageBuilder(VORGANG_ID_2).setId(nachrichtId).build();
-        var event = NachrichtEventTestFactory.createBuilder().rueckfrageId(reply.getId()).build();
-        repo.addReply(reply, event);
-
-        var ids = repo.getAllNachrichtenIds();
-        assertThat(ids).hasSize(4).contains(nachrichtId, NACHRICHT_ID_1_0, NACHRICHT_ID_2_1, NACHRICHT_ID_2_0);
-    }
-
-    @Test
-    void addFileContent() {
-        var fileId = UUID.randomUUID().toString();
-        var fileContent = "This is the content".getBytes();
-        repo.addFileContent(fileId, fileContent);
-
-        assertThat(repo.getFileContent(fileId)).isPresent().hasValue(fileContent);
-    }
-
-    @Test
-    void addFile() {
-        var id = repo.addFile(GrpcUploadBinaryFileMetaData.newBuilder().setFileName("file.txt").setContentType(E2eTestController.CONTENT_TYPE).build());
-
-        assertThat(repo.getFileMetaData(id)).isNotNull();
-    }
-
-    @Test
-    void getUploadedAnswerAttachment() {
-        var id = repo.addFile(GrpcUploadBinaryFileMetaData.newBuilder().setFileName("file.txt").setContentType(E2eTestController.CONTENT_TYPE).build());
-
-        assertThat(repo.getUploadedAnswerAttachment(id)).isNotNull();
-    }
-
-    @Test
-    void findNachricht() {
-        var nachricht = repo.findNachricht(NACHRICHT_ID_2_1);
-
-        assertThat(nachricht).isNotNull();
-    }
-
-    @Test
-    void reset() {
-        var nachrichtId = UUID.randomUUID().toString();
-        var reply = getRueckfrageBuilder(VORGANG_ID_2).setId(nachrichtId).build();
-        var event = NachrichtEventTestFactory.createBuilder().rueckfrageId(reply.getId()).build();
-        repo.addReply(reply, event);
-
-        repo.reset();
-
-        assertThat(repo.getAllNachrichtenIds()).hasSize(3);
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/StubEventRepositoryTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/StubEventRepositoryTest.java
deleted file mode 100644
index bbec5e1dd97da748ac3ba2faa901791d1c237ff6..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/mocks/StubEventRepositoryTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcNachricht;
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcNewNachrichtRequest;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import java.util.UUID;
-
-import static org.assertj.core.api.Assertions.*;
-
-class StubEventRepositoryTest {
-    private StubEventRepository eventRepository;
-
-    @BeforeEach
-    void setUp() {
-        eventRepository = new StubEventRepository();
-    }
-
-    @Nested
-    class TestInitializedEventRepository {
-        @BeforeEach
-        void setUp() {
-            eventRepository.initEvents();
-        }
-
-        @Test
-        void testRemoveNachrichtEvent() {
-            eventRepository.removeNachrichtEvent(StubEventRepository.RUECKFRAGE_ID);
-
-            var events = eventRepository.getNachrichtenEvents(StubEventRepository.POSTFACH_ID);
-            assertThat(events).isEmpty();
-        }
-
-        @Test
-        void testAddNachrichtEvent() {
-            var eventId = UUID.randomUUID().toString();
-            var nachrichtId = UUID.randomUUID().toString();
-            var postfachId = UUID.randomUUID().toString();
-            var vorgangId = UUID.randomUUID().toString();
-            eventRepository.addNachrichtEvent(GrpcNewNachrichtRequest.newBuilder().setNachricht(
-                    GrpcNachricht.newBuilder()
-                            .setEventId(eventId)
-                            .setNachrichtId(nachrichtId)
-                            .setPostfachId(postfachId)
-                            .setVorgangId(vorgangId)
-                            .build()
-            ).build());
-
-            var events = eventRepository.getNachrichtenEvents(postfachId);
-
-            assertThat(events).hasSize(1);
-            assertThat(events.get(0))
-                    .hasFieldOrPropertyWithValue("vorgangId", vorgangId)
-                    .hasFieldOrPropertyWithValue("rueckfrageId", nachrichtId);
-        }
-    }
-
-    @Test
-    void testInitEvents() {
-        eventRepository.initEvents();
-
-        var events = eventRepository.getNachrichtenEvents(StubEventRepository.POSTFACH_ID);
-
-        assertThat(events).hasSize(1);
-        assertThat(events.get(0))
-                .hasFieldOrPropertyWithValue("vorgangId", StubEventRepository.VORGANG_ID)
-                .hasFieldOrPropertyWithValue("rueckfrageId", StubEventRepository.RUECKFRAGE_ID);
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/AttachmentMapperTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/AttachmentMapperTest.java
deleted file mode 100644
index 368c0a0a1d285e1411acb30cc9746186edbd56b9..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/AttachmentMapperTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import org.apache.http.entity.ContentType;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import java.util.UUID;
-
-import static org.assertj.core.api.Assertions.*;
-
-class AttachmentMapperTest {
-    static final String ID = UUID.randomUUID().toString();
-    static final String CONTENT_TYPE = ContentType.APPLICATION_OCTET_STREAM.toString();
-    static final long FILE_SIZE = 10;
-    static final String FILE_NAME = "Testfile.zip";
-
-    GrpcBinaryFile file = GrpcBinaryFile.newBuilder()
-            .setId(ID)
-            .setName(FILE_NAME)
-            .setSize(FILE_SIZE)
-            .setContentType(CONTENT_TYPE)
-            .build();
-
-    @Nested
-    class MapFromGrpcBinaryFile {
-        @Test
-        void shouldMapAttachment() {
-            var res = AttachmentMapper.fromGrpcBinaryFile(file);
-
-            assertThat(res).isNotNull();
-        }
-
-        @Test
-        void shouldMapAttachmentId() {
-            var res = AttachmentMapper.fromGrpcBinaryFile(file).id();
-
-            assertThat(res).isEqualTo(ID);
-        }
-
-        @Test
-        void shouldMapAttachmentName() {
-            var res = AttachmentMapper.fromGrpcBinaryFile(file).fileName();
-
-            assertThat(res).isEqualTo(FILE_NAME);
-        }
-
-        @Test
-        void shouldMapAttachmentSize() {
-            var res = AttachmentMapper.fromGrpcBinaryFile(file).fileSize();
-
-            assertThat(res).isEqualTo(FILE_SIZE);
-        }
-
-        @Test
-        void shouldMapAttachmentContentType() {
-            var res = AttachmentMapper.fromGrpcBinaryFile(file).contentType();
-
-            assertThat(res).isEqualTo(CONTENT_TYPE);
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcPostfachMailTestFactory.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcPostfachMailTestFactory.java
deleted file mode 100644
index a0efe3d4cb0c4ab1fb8a468ce13c7ed9a2ba6874..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcPostfachMailTestFactory.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.GrpcDirection;
-import de.itvsh.ozg.mail.postfach.GrpcPostfachMail;
-import de.itvsh.ozg.pluto.common.GrpcObject;
-import de.itvsh.ozg.pluto.common.GrpcProperty;
-import de.itvsh.ozg.pluto.vorgang.GrpcPostfachAddress;
-
-import java.util.UUID;
-
-public class GrpcPostfachMailTestFactory {
-    static final String NACHRICHT_ID = UUID.randomUUID().toString();
-    static final String VORGANG_ID = NachrichtTestFactory.VORGANG_ID;
-
-    static final String POSTFACH_ID_FIELD = "postfachId";
-
-    static GrpcPostfachMail create() {
-        return createBuilder().build();
-    }
-
-    static GrpcPostfachMail.Builder createBuilder() {
-        return GrpcPostfachMail.newBuilder()
-                .setId(NACHRICHT_ID)
-                .setVorgangId(VORGANG_ID)
-                .setPostfachAddress(createPostfachAddress())
-                .setMailBody(NachrichtTestFactory.TEXT)
-                .setSubject(NachrichtTestFactory.VORGANG_TITLE)
-                .setDirection(GrpcDirection.OUT)
-                .setSentAt(NachrichtTestFactory.DATE_STRING)
-                .setCreatedAt(NachrichtTestFactory.DATE_STRING)
-                .setReplyOption(NachrichtTestFactory.REPLY_FORBIDDEN.name())
-                .addAttachment(NachrichtTestFactory.ATTACHMENT_ID_1)
-                .addAttachment(NachrichtTestFactory.ATTACHMENT_ID_2);
-    }
-
-    private static GrpcPostfachAddress createPostfachAddress() {
-        return GrpcPostfachAddress.newBuilder()
-                .setIdentifier(GrpcObject.newBuilder()
-                        .addProperty(
-                                GrpcProperty.newBuilder()
-                                        .setName(POSTFACH_ID_FIELD)
-                                        .addValue(NachrichtTestFactory.POSTFACH_ID).build()
-                        ).build()
-                ).build();
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcRueckfragenTestFactory.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcRueckfragenTestFactory.java
deleted file mode 100644
index 327fc861cc5d9d1a7eba0a1fb72d0c65241ed5bb..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcRueckfragenTestFactory.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.GrpcFindRueckfragenResponse;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrage;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrageAnswer;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import org.apache.http.entity.ContentType;
-
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.List;
-import java.util.UUID;
-
-public class GrpcRueckfragenTestFactory {
-    static final String RUECKFRAGE_ID = UUID.randomUUID().toString();
-    static final String VORGANG_ID = UUID.randomUUID().toString();
-    static final String VORGANG_NAME = NachrichtTestFactory.VORGANG_TITLE;
-    static final String CONTENT_TYPE = ContentType.APPLICATION_OCTET_STREAM.toString();
-    static final long FILE_SIZE = 10;
-    static final String FILE_NAME = "Testfile.zip";
-
-    public static final String TEXT = """
-            Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
-             sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
-             sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
-             
-             Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-             Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
-            """;
-
-    public static final String ANSWER_TEXT = """
-            ANTWORT Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-             Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
-            """;
-
-    public static final String SEND_AT = "2022-10-26T09:26:25";
-
-    public static final long SEND_AT_DATE = LocalDateTime.parse(SEND_AT).toEpochSecond(ZoneOffset.UTC);
-
-    public static final String ANSWERED_AT = "2022-10-27T12:00:00";
-
-    public static final long ANSWERED_AT_DATE = LocalDateTime.parse(ANSWERED_AT).toEpochSecond(ZoneOffset.UTC);
-
-    static GrpcFindRueckfragenResponse createFindRueckfragenResponse() {
-        return GrpcFindRueckfragenResponse.newBuilder().addRueckfrage(
-                        createRuechfrageBuilder()
-                                .build())
-                .build();
-    }
-
-    static GrpcRueckfrage create() {
-        return createRuechfrageBuilder().build();
-    }
-
-    static GrpcRueckfrage createWithReply() {
-        return createRuechfrageBuilder()
-                .setAnsweredAt(ANSWERED_AT)
-                .addAnswers(GrpcRueckfrageAnswer.newBuilder()
-                        .setAnswerText(ANSWER_TEXT)
-                        .addAnswerAttachment(GrpcBinaryFile.getDefaultInstance())
-                        .build())
-                .build();
-    }
-
-    static GrpcRueckfrage.Builder createRuechfrageBuilder() {
-        return GrpcRueckfrage.newBuilder()
-                .setId(RUECKFRAGE_ID)
-                .setVorgangId(VORGANG_ID)
-                .setVorgangName(VORGANG_NAME)
-                .setText(TEXT)
-                .setSendAt(SEND_AT)
-                .addAllAttachment(List.of(createBinaryFile(NachrichtTestFactory.ATTACHMENT_ID_1), createBinaryFile(NachrichtTestFactory.ATTACHMENT_ID_2)));
-    }
-
-    static GrpcBinaryFile createBinaryFile(String attachmentId) {
-        return GrpcBinaryFile.newBuilder()
-                .setContentType(CONTENT_TYPE)
-                .setSize(FILE_SIZE)
-                .setId(attachmentId)
-                .setName(FILE_NAME)
-                .build();
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtEventTestFactory.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtEventTestFactory.java
deleted file mode 100644
index e38452d0a2feb28b65949c38da615f30bba903eb..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtEventTestFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-
-import java.util.UUID;
-
-public class NachrichtEventTestFactory {
-    public static final String NACHRICHT_ID = UUID.randomUUID().toString();
-    public static final String POSTFACH_ID = UUID.randomUUID().toString();
-    public static final String VORGANG_ID = UUID.randomUUID().toString();
-    public static final String URL = "http://localhost";
-
-    public static NachrichtEvent create() {
-        return createBuilder().build();
-    }
-
-    public static NachrichtEvent.NachrichtEventBuilder createBuilder() {
-        return NachrichtEvent.builder()
-                .rueckfrageId(NACHRICHT_ID)
-                .postfachId(POSTFACH_ID)
-                .vorgangId(VORGANG_ID);
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtMapperTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtMapperTest.java
deleted file mode 100644
index 21797e9719f11139e988aba7b01246880d856dd3..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtMapperTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrage;
-import de.mgm.bup.ozg.antragsraum.attachments.OzgFile;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-import java.util.UUID;
-
-import static org.assertj.core.api.Assertions.*;
-
-class NachrichtMapperTest {
-
-    @Nested
-    class TestFromGrpcMail {
-        private final GrpcRueckfrage rueckfrage = GrpcRueckfragenTestFactory.create();
-        private final GrpcRueckfrage answeredRueckfrage = GrpcRueckfragenTestFactory.createWithReply();
-
-        @Test
-        void shouldMapNachrichtId() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.createRuechfrageBuilder().setId(NachrichtTestFactory.ID).build());
-
-            assertThat(nachricht.id()).isEqualTo(NachrichtTestFactory.ID);
-        }
-
-        @Test
-        void shouldMapVorgangId() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(rueckfrage);
-
-            assertThat(nachricht.vorgangId()).isEqualTo(GrpcRueckfragenTestFactory.VORGANG_ID);
-        }
-
-        @Test
-        void shouldMapVorgangName() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(rueckfrage);
-
-            assertThat(nachricht.topicTitle()).isEqualTo(NachrichtTestFactory.VORGANG_TITLE);
-        }
-
-        @Test
-        void shouldMapNachrichtText() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(rueckfrage);
-
-            assertThat(nachricht.message()).isEqualTo(GrpcRueckfragenTestFactory.TEXT);
-        }
-
-        @Test
-        void shouldMapCreationDate() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(rueckfrage);
-
-            assertThat(nachricht.date()).isEqualTo(GrpcRueckfragenTestFactory.SEND_AT_DATE);
-        }
-
-        @Test
-        void shouldMapInvalidCreationDate() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.createRuechfrageBuilder().setSendAt("huu").build());
-
-            assertThat(nachricht.date()).isZero();
-        }
-
-        @Test
-        void shouldMapAttachments() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.create());
-
-            assertThat(nachricht.attachments()).hasSize(2);
-        }
-
-        @Test
-        void shouldNotHaveAnswersWhenUnanswered() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.create());
-
-            assertThat(nachricht.replyNachrichten()).isEmpty();
-        }
-
-        @Test
-        void shouldMapAnswerNachrichtText() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(answeredRueckfrage);
-
-            assertThat(nachricht.replyNachrichten().get(0).message()).isEqualTo(GrpcRueckfragenTestFactory.ANSWER_TEXT);
-        }
-
-        @Test
-        void shouldMapAnsweredDate() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(answeredRueckfrage);
-
-            assertThat(nachricht.replyNachrichten().get(0).date()).isEqualTo(GrpcRueckfragenTestFactory.ANSWERED_AT_DATE);
-        }
-
-        @Test
-        void shouldMapAnswerAttachments() {
-            var nachricht = NachrichtMapper.fromGrpcRueckfrage(answeredRueckfrage);
-
-            assertThat(nachricht.replyNachrichten().get(0).attachments()).hasSize(1);
-        }
-    }
-
-    @Nested
-    class TestToGrpcRueckfrageAnswer {
-        @Test
-        void shouldMapNachrichtText() {
-            var nachricht = NachrichtMapper.toGrpcRueckfrageAnswer(ReplyNachrichtTestFactory.create());
-
-            assertThat(nachricht.getAnswerText()).isEqualTo(ReplyNachrichtTestFactory.TEXT);
-        }
-
-        @Test
-        void shouldMapEmptyAttachments() {
-            var nachricht = NachrichtMapper.toGrpcRueckfrageAnswer(ReplyNachrichtTestFactory.create());
-
-            assertThat(nachricht.getAnswerAttachmentList()).isEmpty();
-        }
-
-        @Test
-        void shouldMapAttachments() {
-            var nachricht = NachrichtMapper.toGrpcRueckfrageAnswer(
-                    ReplyNachrichtTestFactory.createBuilder()
-                            .attachments(List.of(OzgFile.builder().id(UUID.randomUUID().toString()).build())).build());
-
-            assertThat(nachricht.getAnswerAttachmentList()).hasSize(1);
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtTestFactory.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtTestFactory.java
deleted file mode 100644
index 16683e7e24c698dae05916b8f8045b8ade5d6a8b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtTestFactory.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.List;
-import java.util.UUID;
-
-public class NachrichtTestFactory {
-    public static final String ID = UUID.randomUUID().toString();
-    public static final String POSTFACH_ID = UUID.randomUUID().toString();
-    public static final String VORGANG_ID = UUID.randomUUID().toString();
-    public static final String TEXT = """
-            Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
-             sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
-             sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
-             
-             Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-             Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
-            """;
-    public static final String VORGANG_TITLE = "Vorgang Test ";
-    public static final String ATTACHMENT_ID_1 = "6358fd3f46811d04010f44c8";
-    public static final String ATTACHMENT_ID_2 = "6358fd4146811d04010f44d0";
-    public static final String DATE_STRING = "2022-10-26T09:26:25";
-    public static final long DATE = LocalDateTime.parse(DATE_STRING).toEpochSecond(ZoneOffset.UTC);
-    public static final ReplyOption REPLY_FORBIDDEN = ReplyOption.FORBIDDEN;
-    public static final ReplyOption REPLY_ALLOWED = ReplyOption.ALLOWED;
-    public static final String REPLY_TO_NACHRICHT_ID = UUID.randomUUID().toString();
-
-    public static Nachricht create() {
-        return createBuilder().build();
-    }
-
-    public static Nachricht.NachrichtBuilder createBuilder() {
-        return Nachricht.builder()
-                .id(ID)
-                .message(TEXT)
-                .topicTitle(VORGANG_TITLE)
-                .vorgangId(VORGANG_ID)
-                .postfachId(POSTFACH_ID)
-                .replyOption(REPLY_ALLOWED)
-                .replyNachrichten(List.of(ReplyNachricht.builder().id(ID).build()))
-                .date(DATE);
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerITCase.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerITCase.java
deleted file mode 100644
index a843f8032362f0c12049edc52f1009a48d46f988..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerITCase.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import com.c4_soft.springaddons.security.oauth2.test.annotations.WithJwt;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import de.mgm.bup.ozg.antragsraum.Oauth2TestConfiguration;
-import de.mgm.bup.ozg.antragsraum.common.AddressNotFoundException;
-import de.mgm.bup.ozg.antragsraum.common.SendTimeoutException;
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.boot.test.mock.mockito.SpyBean;
-import org.springframework.http.MediaType;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.ResultActions;
-
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-import static org.mockito.Mockito.*;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-@AutoConfigureMockMvc
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
-@SpringJUnitConfig(classes = {NachrichtenControllerTestConfiguration.class, Oauth2TestConfiguration.class})
-public class NachrichtenControllerITCase {
-    @SpyBean
-    private NachrichtenController nachrichtenController;
-
-    @Autowired
-    private MockMvc mockMvc;
-
-    @MockBean
-    private NachrichtenService nachrichtenService;
-
-    @Nested
-    class TestLoadingNachrichten {
-        @BeforeEach
-        void init() {
-            when(nachrichtenService.getTopicsOfPostfach(anyString())).thenReturn(
-                    TopicMapper.toTopics(List.of(NachrichtTestFactory.create()))
-            );
-        }
-
-        @Test
-        @WithJwt("user.json")
-        void shouldLoadRueckfragen() throws Exception {
-            performRequest()
-                    .andExpect(status().isOk())
-                    .andExpect(jsonPath("$[0]['clerkMessage']['id']").value(NachrichtTestFactory.ID));
-        }
-
-        @Test
-        void shouldNotLoadRueckfragenUnauthorized() throws Exception {
-            performRequest()
-                    .andExpect(status().isUnauthorized());
-        }
-
-        ResultActions performRequest() throws Exception {
-            return mockMvc.perform(
-                    get(NachrichtenController.PATH + "/nachrichten/" + NachrichtTestFactory.POSTFACH_ID)
-                            .contentType(MediaType.APPLICATION_JSON).characterEncoding(Charset.defaultCharset()));
-        }
-    }
-
-    @Nested
-    class TestHandlingExceptions {
-
-        @Test
-        @WithJwt("user.json")
-        void shouldHandleRuntimeExceptions() throws Exception {
-            doThrow(RuntimeException.class).when(nachrichtenService).getTopicsOfPostfach(anyString());
-
-            performRequest().andExpect(status().isInternalServerError());
-        }
-
-        @Test
-        @WithJwt("user.json")
-        void shouldHandleTechnicalException() throws Exception {
-            doThrow(TechnicalException.class).when(nachrichtenService).getTopicsOfPostfach(anyString());
-
-            performRequest().andExpect(status().isInternalServerError());
-        }
-
-        ResultActions performRequest() throws Exception {
-            return mockMvc.perform(
-                    get(NachrichtenController.PATH + "/nachrichten/" + NachrichtTestFactory.POSTFACH_ID)
-                            .contentType(MediaType.APPLICATION_JSON).characterEncoding(Charset.defaultCharset()));
-        }
-    }
-
-    @Nested
-    class TestSendingNachricht {
-        private final ObjectMapper mapper = new ObjectMapper();
-        private final ReplyNachricht msg = ReplyNachrichtTestFactory.create();
-
-        @BeforeEach
-        void init() {
-            doReturn(msg).when(nachrichtenService).sendRueckfrageAnswer(any(ReplyNachricht.class), anyString());
-        }
-
-        @Test
-        void shouldSendNachricht() throws Exception {
-            performPutRequest(ReplyNachrichtTestFactory.ID, mapper.writeValueAsString(msg))
-                    .andExpect(status().isOk())
-                    .andExpect(jsonPath("$.id").value(ReplyNachrichtTestFactory.ID));
-        }
-
-        @Test
-        void shouldNotSendNachrichtNoId() throws Exception {
-            performPutRequest("", mapper.writeValueAsString(msg))
-                    .andExpect(status().isNotFound());
-        }
-
-        @Test
-        void shouldNotSendNachrichtBlankId() throws Exception {
-            var invalidMsg = ReplyNachrichtTestFactory.createBuilder().id("").build();
-
-            performPutRequest(ReplyNachrichtTestFactory.ID, mapper.writeValueAsString(invalidMsg))
-                    .andExpect(status().isBadRequest());
-        }
-
-        @Test
-        void shouldNotSendNachrichtNoAddress() throws Exception {
-            doThrow(AddressNotFoundException.class).when(nachrichtenService).sendRueckfrageAnswer(any(), anyString());
-
-            performPutRequest(ReplyNachrichtTestFactory.ID, mapper.writeValueAsString(msg))
-                    .andExpect(status().isNotFound());
-        }
-
-        @Test
-        void shouldNotSendNachrichtTimeout() throws Exception {
-            doThrow(new SendTimeoutException("test", new TimeoutException())).when(nachrichtenService).sendRueckfrageAnswer(any(), anyString());
-
-            performPutRequest(ReplyNachrichtTestFactory.ID, mapper.writeValueAsString(msg))
-                    .andExpect(status().isGatewayTimeout());
-        }
-
-        ResultActions performPutRequest(String id, String body) throws Exception {
-            return mockMvc.perform(
-                    put(NachrichtenController.PATH + "/nachricht/" + id)
-                            .with(jwt().authorities(List.of(new SimpleGrantedAuthority("admin"), new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL")))
-                                    .jwt(jwt -> jwt.claim(StandardClaimNames.PREFERRED_USERNAME, "janedoe")))
-                            .with(csrf().asHeader())
-                            .contentType(MediaType.APPLICATION_JSON)
-                            .characterEncoding(Charset.defaultCharset())
-                            .content(body));
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerTest.java
deleted file mode 100644
index 9d7fc63930a655addf4ccd9edf8e50f799c41d08..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class NachrichtenControllerTest {
-    @Spy
-    @InjectMocks
-    private NachrichtenController nachrichtenController;
-
-    @Mock
-    private NachrichtenService nachrichtenService;
-
-    @Nested
-    class TestLoadingTopic {
-
-        @BeforeEach
-        void init() {
-            when(nachrichtenService.getTopicsOfPostfach(anyString())).thenReturn(
-                    TopicMapper.toTopics(List.of(NachrichtTestFactory.create()))
-            );
-        }
-
-        @Test
-        void shouldLoadTopics() {
-            var result = nachrichtenController.getTopicsOfPostfach(NachrichtTestFactory.POSTFACH_ID);
-
-            assertThat(result).hasSize(1);
-        }
-
-        @Test
-        void shouldCallGetTopicsOfPostfach() {
-            nachrichtenController.getTopicsOfPostfach(NachrichtTestFactory.POSTFACH_ID);
-
-            verify(nachrichtenService).getTopicsOfPostfach(anyString());
-        }
-    }
-
-    @Nested
-    class TestSendingAnswer {
-        @Test
-        void shouldCallService() {
-            nachrichtenController.sendNachricht(NachrichtTestFactory.REPLY_TO_NACHRICHT_ID, ReplyNachrichtTestFactory.create());
-
-            verify(nachrichtenService).sendRueckfrageAnswer(any(ReplyNachricht.class), anyString());
-        }
-
-        @Test
-        void shouldThrowExceptionOnEmptyReplyToNachrichtId() {
-            var nachricht = ReplyNachrichtTestFactory.create();
-            assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(
-                    () -> nachrichtenController.sendNachricht(null, nachricht));
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenGrpcClientTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenGrpcClientTest.java
deleted file mode 100644
index 942eb1902c7ef481cb9887380f0cba0a22c02cc8..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenGrpcClientTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import com.google.common.collect.ImmutableList;
-import de.itvsh.ozg.mail.postfach.*;
-import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc;
-import de.itvsh.ozg.pluto.grpc.command.GrpcGetCommandRequest;
-import de.mgm.bup.ozg.antragsraum.common.ChannelPropertiesFactory;
-import io.grpc.Channel;
-import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
-import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
-import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class NachrichtenGrpcClientTest {
-    private final static String CHANNEL_ADDRESS = "static://localhost:9090";
-    @Spy
-    @InjectMocks
-    private NachrichtenGrpcClient grpcClient;
-
-    @Mock
-    private GlobalClientInterceptorRegistry registry;
-
-    @Mock
-    private ChannelPropertiesFactory channelPropertiesFactory;
-
-    @Nested
-    class TestCreatingChannel {
-        @BeforeEach
-        void init() {
-            when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(new GrpcChannelsProperties());
-        }
-
-        @Test
-        void shouldGetChannelFactory() {
-            var channelFactory = grpcClient.getChannelFactory(CHANNEL_ADDRESS);
-
-            assertThat(channelFactory).isNotNull();
-        }
-
-        @Test
-        void shouldGetChannel() {
-            when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
-
-            var channel = grpcClient.getChannelFactory(CHANNEL_ADDRESS).createChannel(CHANNEL_ADDRESS);
-
-            assertThat(channel).isNotNull();
-        }
-    }
-
-    @Nested
-    class TestSendingRequests {
-        @Mock
-        private PostfachServiceGrpc.PostfachServiceBlockingStub postfachServiceBlockingStub;
-
-        @Nested
-        class TestRueckfrageAnswer {
-            @Mock
-            private GrpcRueckfrageAnswerRequest request;
-
-            @Test
-            void shouldGetGrpcSendPostfachMailResponse() {
-                grpcClient.getAnswerResponse(request, postfachServiceBlockingStub);
-
-                verify(postfachServiceBlockingStub).sendRueckfrageAnswer(any(GrpcRueckfrageAnswerRequest.class));
-            }
-        }
-
-        @Nested
-        class TestFindRueckfragen {
-            @Mock
-            private GrpcFindRueckfragenRequest request;
-
-            @Test
-            void shouldCallClientFindRueckfragen() {
-                var channelFactory = mock(GrpcChannelFactory.class);
-                var channel = mock(Channel.class);
-                when(channelFactory.createChannel(anyString())).thenReturn(channel);
-                doReturn(channelFactory).when(grpcClient).getChannelFactory(anyString());
-                doReturn(GrpcFindRueckfragenResponse.getDefaultInstance()).when(grpcClient).getGrpcFindRueckfragenResponse(any(GrpcFindRueckfragenRequest.class), any());
-
-                grpcClient.getFindRueckfragen(request, CHANNEL_ADDRESS);
-
-                verify(grpcClient).getGrpcFindRueckfragenResponse(any(GrpcFindRueckfragenRequest.class), any());
-            }
-
-            @Test
-            void shouldCallRemoteServiceFindRueckfragen() {
-                grpcClient.getGrpcFindRueckfragenResponse(request, postfachServiceBlockingStub);
-
-                verify(postfachServiceBlockingStub).findRueckfragen(any(GrpcFindRueckfragenRequest.class));
-            }
-        }
-
-        @Nested
-        class TestGetRueckfrage {
-            @Mock
-            private GrpcGetRueckfrageRequest request;
-
-            @Test
-            void shouldCallRemoteServiceGetRueckfrage() {
-                grpcClient.getGrpcGetRueckfrageResponse(request, postfachServiceBlockingStub);
-
-                verify(postfachServiceBlockingStub).getRueckfrage(any(GrpcGetRueckfrageRequest.class));
-            }
-        }
-    }
-
-    @Nested
-    class TestCheckingCommandStatus {
-        @Mock
-        private CommandServiceGrpc.CommandServiceBlockingStub commandServiceBlockingStub;
-        @Mock
-        private GrpcGetCommandRequest request;
-
-        @Test
-        void shouldGetCommand() {
-            grpcClient.getGrpcCommand(commandServiceBlockingStub, request);
-            verify(commandServiceBlockingStub).getCommand(any(GrpcGetCommandRequest.class));
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteServiceITCase.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteServiceITCase.java
deleted file mode 100644
index 4da251a762da888642cc45928f5c458524aa4423..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteServiceITCase.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-
-import de.mgm.bup.ozg.antragsraum.common.SendTimeoutException;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.SpyBean;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@SpringBootTest(properties = {
-        "grpc.server.inProcessName=test2",
-        "grpc.server.port=-1"
-})
-@SpringJUnitConfig(classes = {NachrichtenServiceGrpcTestConfiguration.class})
-@DirtiesContext
-@ExtendWith(MockitoExtension.class)
-class NachrichtenRemoteServiceITCase {
-    public static final String ADDRESS = "in-process:test2";
-    public static final String COMMAND_ID = "id";
-
-    @SpyBean
-    NachrichtenRemoteService nachrichtenRemoteService;
-
-    @Nested
-    class TestGetNachrichten {
-        @Test
-        void shouldLoadNachrichten() {
-            var nachricht = nachrichtenRemoteService.getRueckfragen(NachrichtEventTestFactory.create(), ADDRESS);
-
-            assertThat(nachricht).isNotNull();
-        }
-
-        @Test
-        void shouldHaveRueckfrageId() {
-            var event = NachrichtEventTestFactory.createBuilder()
-                    .postfachId(NachrichtTestFactory.POSTFACH_ID)
-                    .rueckfrageId(GrpcRueckfragenTestFactory.RUECKFRAGE_ID).build();
-
-            var nachrichten = nachrichtenRemoteService.getRueckfragen(event, ADDRESS);
-
-            assertThat(nachrichten.get(0).id()).isEqualTo(GrpcRueckfragenTestFactory.RUECKFRAGE_ID);
-        }
-    }
-
-    @Nested
-    class TestSendNachricht {
-        @Test
-        void shouldSendNachricht() {
-            var res = nachrichtenRemoteService.sendAnswer(ReplyNachrichtTestFactory.create(), ADDRESS);
-
-            assertThat(res).isTrue();
-        }
-
-        @Test
-        void shouldCheckCommand() throws Exception {
-            SecurityContextHolder.getContext().setAuthentication(mock(Authentication.class));
-
-            nachrichtenRemoteService.sendAnswer(ReplyNachrichtTestFactory.create(), ADDRESS);
-
-            verify(nachrichtenRemoteService).waitUntilCommandIsFinished(anyString(), anyString());
-            verify(nachrichtenRemoteService).checkCommandIsFinished(anyString(), anyString(), any(Authentication.class));
-        }
-
-        @Test
-        void shouldThrowSendTimeoutException() {
-            assertThatExceptionOfType(SendTimeoutException.class).isThrownBy(() ->
-                    nachrichtenRemoteService.waitUntilCommandIsFinished(ADDRESS, COMMAND_ID)
-            );
-        }
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteServiceTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteServiceTest.java
deleted file mode 100644
index 61a25d22daa12eae31d5ba29c63b93cc4495c08b..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenRemoteServiceTest.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.GrpcGetRueckfrageRequest;
-import de.itvsh.ozg.mail.postfach.GrpcGetRueckfrageResponse;
-import de.itvsh.ozg.mail.postfach.GrpcRueckfrageAnswerRequest;
-import de.mgm.bup.ozg.antragsraum.common.AddressNotFoundException;
-import de.mgm.bup.ozg.antragsraum.common.SendTimeoutException;
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import io.grpc.StatusRuntimeException;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.time.Duration;
-import java.time.temporal.ChronoUnit;
-import java.util.concurrent.ExecutionException;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-public class NachrichtenRemoteServiceTest {
-
-    public static final String ADDRESS = "test";
-    @Spy
-    @InjectMocks
-    private NachrichtenRemoteService nachrichtenRemoteService;
-
-    @Mock
-    private NachrichtenGrpcClient nachrichtenGrpcClient;
-
-    @Mock
-    private NachrichtProperties nachrichtProperties;
-
-    @Nested
-    class TestNachrichtenAvailable {
-        private NachrichtEvent event;
-
-        @BeforeEach
-        void init() {
-            when(nachrichtenGrpcClient.getRueckfrage(any(GrpcGetRueckfrageRequest.class), anyString())).thenReturn(
-                    GrpcGetRueckfrageResponse.newBuilder().setRueckfrage(GrpcRueckfragenTestFactory.create()).build()
-            );
-
-            event = NachrichtEventTestFactory.createBuilder().rueckfrageId(GrpcRueckfragenTestFactory.RUECKFRAGE_ID).build();
-        }
-
-        @Test
-        void shouldLoadNachrichtById() {
-            var nachricht = nachrichtenRemoteService.getRueckfragen(event, ADDRESS);
-
-            assertThat(nachricht).isNotEmpty();
-        }
-    }
-
-    @Nested
-    class TestNoNachrichtenAvailable {
-        @BeforeEach
-        void init() {
-            doThrow(StatusRuntimeException.class).when(nachrichtenGrpcClient).getRueckfrage(any(GrpcGetRueckfrageRequest.class), anyString());
-        }
-
-        @Test
-        void shouldHandleStatusRuntimeException() {
-            var nachricht = nachrichtenRemoteService.getRueckfragen(NachrichtEventTestFactory.create(), ADDRESS);
-
-            assertThat(nachricht).isEmpty();
-        }
-    }
-
-    @Nested
-    class TestSendAnswer {
-        private final ReplyNachricht reply = ReplyNachrichtTestFactory.create();
-
-        @BeforeEach
-        void init() {
-            when(nachrichtenGrpcClient.answerRueckfrage(any(GrpcRueckfrageAnswerRequest.class), anyString())).thenReturn(
-                    GrpcCommandTestFactory.createBuilder().setStatus(GrpcCommandTestFactory.STATUS_FINISHED).build()
-            );
-        }
-
-        @Test
-        void shouldSendAnswer() {
-            assertThat(nachrichtenRemoteService.sendAnswer(reply, ADDRESS)).isTrue();
-        }
-    }
-
-    @Nested
-    class TestSendAnswerDelayed {
-        private final ReplyNachricht reply = ReplyNachrichtTestFactory.create();
-
-        @BeforeEach
-        void init() {
-            when(nachrichtenGrpcClient.answerRueckfrage(any(GrpcRueckfrageAnswerRequest.class), anyString())).thenReturn(
-                    GrpcCommandTestFactory.create()
-            );
-        }
-
-        @Test
-        void shouldSendAnswer() {
-            when(nachrichtenGrpcClient.getCommand(anyString(), anyString())).thenReturn(GrpcCommandTestFactory.createBuilder().setStatus(GrpcCommandTestFactory.STATUS_FINISHED).build());
-            when(nachrichtProperties.getSendPollIntervalDuration()).thenReturn(Duration.of(1L, ChronoUnit.SECONDS));
-            when(nachrichtProperties.getTimeoutDuration()).thenReturn(Duration.of(2L, ChronoUnit.SECONDS));
-
-            assertThat(nachrichtenRemoteService.sendAnswer(reply, ADDRESS)).isTrue();
-        }
-
-        @Test
-        void shouldThrowTimeoutException() {
-            assertThatExceptionOfType(SendTimeoutException.class).isThrownBy(() -> nachrichtenRemoteService.sendAnswer(reply, ADDRESS));
-        }
-
-        @Test
-        void shouldThrowTechnicalExceptionBecauseOfInterruptedException() throws Exception {
-            doThrow(InterruptedException.class).when(nachrichtenRemoteService).getSendResult(any());
-
-            assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> nachrichtenRemoteService.sendAnswer(reply, ADDRESS));
-        }
-
-        @Test
-        void shouldThrowTechnicalExceptionBecauseOfExecutionException() throws Exception {
-            doThrow(ExecutionException.class).when(nachrichtenRemoteService).getSendResult(any());
-
-            assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> nachrichtenRemoteService.sendAnswer(reply, ADDRESS));
-        }
-    }
-
-    @Nested
-    class TestNoTargetAddressAvailable {
-        private NachrichtEvent event;
-        private String address;
-
-        @BeforeEach
-        void init() {
-            doThrow(AddressNotFoundException.class).when(nachrichtenGrpcClient).getRueckfrage(any(GrpcGetRueckfrageRequest.class), anyString());
-            event = NachrichtEventTestFactory.create();
-            address = "static://localhost:9090";
-        }
-
-        @Test
-        void shouldThrowAddressNotFoundException() {
-            assertThatExceptionOfType(AddressNotFoundException.class).isThrownBy(
-                    () -> nachrichtenRemoteService.getRueckfragen(event, address)
-            );
-        }
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenServiceGrpcTestConfiguration.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenServiceGrpcTestConfiguration.java
deleted file mode 100644
index d6232b71ff7a876be2ff251b33deb0d257bd1906..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenServiceGrpcTestConfiguration.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import com.google.common.collect.ImmutableList;
-import de.mgm.bup.ozg.antragsraum.common.ChannelPropertiesFactory;
-import lombok.NonNull;
-import net.devh.boot.grpc.client.autoconfigure.*;
-import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
-import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
-import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
-import net.devh.boot.grpc.common.autoconfigure.GrpcCommonTraceAutoConfiguration;
-import net.devh.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration;
-import net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import static org.mockito.Mockito.*;
-
-@Configuration
-@ImportAutoConfiguration({
-        GrpcCommonCodecAutoConfiguration.class,
-        GrpcCommonTraceAutoConfiguration.class,
-
-        GrpcServerAutoConfiguration.class,
-        GrpcServerFactoryAutoConfiguration.class,
-
-        GrpcClientAutoConfiguration.class,
-        GrpcClientHealthAutoConfiguration.class,
-        GrpcClientMetricAutoConfiguration.class,
-        GrpcClientSecurityAutoConfiguration.class,
-        GrpcClientTraceAutoConfiguration.class
-})
-public class NachrichtenServiceGrpcTestConfiguration {
-    @Bean
-    PostfachMailGrpcService postfachServiceGrpcStub() {
-        return new PostfachMailGrpcService();
-    }
-
-    @Bean
-    CommandGrpcService commandGrpcServiceStub() {
-        return new CommandGrpcService();
-    }
-
-    NachrichtProperties nachrichtProperties() {
-        NachrichtProperties nachrichtProperties = new NachrichtProperties();
-        nachrichtProperties.setSendPollInterval("PT-0.5S");
-        nachrichtProperties.setSendTimeout("PT2S");
-        return nachrichtProperties;
-    }
-
-    @Bean
-    NachrichtenRemoteService nachrichtRemoteService() {
-        return new NachrichtenRemoteService(nachrichtenGrpcClient(), nachrichtProperties());
-    }
-
-    NachrichtenGrpcClient nachrichtenGrpcClient() {
-        return new NachrichtenGrpcClient(initGlobalClientInterceptorRegistry(), initChannelsPropertiesFactory());
-    }
-
-    @NonNull
-    private static GlobalClientInterceptorRegistry initGlobalClientInterceptorRegistry() {
-        GlobalClientInterceptorRegistry globalClientInterceptorRegistry = mock(GlobalClientInterceptorRegistry.class);
-        when(globalClientInterceptorRegistry.getClientInterceptors()).thenReturn(ImmutableList.of());
-
-        return globalClientInterceptorRegistry;
-    }
-
-    @NonNull
-    private static ChannelPropertiesFactory initChannelsPropertiesFactory() {
-        return new ChannelPropertiesFactory(new GrpcChannelsProperties());
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenServiceTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenServiceTest.java
deleted file mode 100644
index e559eb4941afcf35e2604b5a5348cbab367060c7..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenServiceTest.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.mgm.bup.ozg.antragsraum.attachments.OzgFile;
-import de.mgm.bup.ozg.antragsraum.common.AddressNotFoundException;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEvent;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEventService;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.Spy;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith(MockitoExtension.class)
-class NachrichtenServiceTest {
-    @Spy
-    @InjectMocks
-    private NachrichtenService nachrichtenService;
-    @Mock
-    private NachrichtEventService nachrichtEventService;
-    @Mock
-    private NachrichtenRemoteService nachrichtenRemoteService;
-
-    @Nested
-    class TestNachrichtenEventAvailable {
-        final NachrichtEvent event = NachrichtEventTestFactory.create();
-
-        @BeforeEach
-        void init() {
-            when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(
-                    List.of(event)
-            );
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(
-                    "test"
-            );
-
-            when(nachrichtenRemoteService.getRueckfragen(any(), anyString())).thenReturn(
-                    List.of(NachrichtTestFactory.createBuilder()
-                            .vorgangId(event.vorgangId())
-                            .postfachId(event.postfachId())
-                            .id(event.rueckfrageId())
-                            .attachments(List.of(OzgFile.builder().id(NachrichtTestFactory.ATTACHMENT_ID_1).build()))
-                            .replyNachrichten(List.of(ReplyNachrichtTestFactory.createBuilder().id(event.rueckfrageId()).build()))
-                            .build())
-            );
-        }
-
-        @Test
-        void shouldCallNachrichtRemoteService() {
-            nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            verify(nachrichtenRemoteService, atMostOnce()).getRueckfragen(any(NachrichtEvent.class), anyString());
-        }
-
-        @Test
-        void shouldHaveNachrichten() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            assertThat(nachrichten).hasSize(1);
-        }
-
-        @Test
-        void shouldHaveNachrichtenText() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::message).isNotNull();
-        }
-
-        @Test
-        void shouldHavePostfachId() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::postfachId).hasValue(event.postfachId());
-        }
-
-        @Test
-        void shouldHaveVorgangId() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::vorgangId).hasValue(event.vorgangId());
-        }
-
-        @Test
-        void shouldHaveRueckfrageId() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::id).hasValue(event.rueckfrageId());
-        }
-
-        @Test
-        void shouldHaveAttachment() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::attachments).hasValue(List.of(OzgFile.builder().id(NachrichtTestFactory.ATTACHMENT_ID_1).build()));
-        }
-
-        @Test
-        void shouldMapToTopics() {
-            var res = nachrichtenService.getTopicsOfPostfach(event.postfachId());
-
-            assertThat(res).isNotEmpty();
-        }
-    }
-
-    @Nested
-    class TestNachrichtWithNullAttachment {
-        final NachrichtEvent event = NachrichtEventTestFactory.create();
-
-        @BeforeEach
-        void init() {
-            when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(
-                    List.of(event)
-            );
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(
-                    "test"
-            );
-
-            when(nachrichtenRemoteService.getRueckfragen(any(NachrichtEvent.class), anyString())).thenReturn(
-                    List.of(NachrichtTestFactory.createBuilder()
-                            .vorgangId(event.vorgangId())
-                            .postfachId(event.postfachId())
-                            .id(event.rueckfrageId())
-                            .build())
-            );
-        }
-
-        @Test
-        void shouldNotHaveAttachment() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
-
-            assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::attachments).isEmpty();
-        }
-    }
-
-    @Nested
-    class TestNoNachrichtenEventAvailable {
-        @BeforeEach
-        void init() {
-            Mockito.when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(
-                    List.of()
-            );
-        }
-
-        @Test
-        void shouldCallNachrichtEventService() {
-            var nachrichten = nachrichtenService.getRueckfragenOfPostfach(NachrichtTestFactory.POSTFACH_ID);
-
-            assertThat(nachrichten).isEmpty();
-        }
-
-        @Test
-        void shouldNotCallNachrichtRemoteService() {
-            nachrichtenService.getRueckfragenOfPostfach(NachrichtTestFactory.POSTFACH_ID);
-
-            verify(nachrichtenRemoteService, never()).getRueckfragen(any(NachrichtEvent.class), anyString());
-        }
-    }
-
-    @Nested
-    class TestSendNachricht {
-        ReplyNachricht msg = ReplyNachrichtTestFactory.create();
-
-        @Test
-        void shouldCallEventService() {
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn("test");
-
-            nachrichtenService.sendRueckfrageAnswer(msg, NachrichtTestFactory.REPLY_TO_NACHRICHT_ID);
-
-            verify(nachrichtEventService).getServiceUrlOfNachricht(anyString());
-        }
-
-        @Test
-        void shouldCallRemoteService() {
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn("test");
-
-            nachrichtenService.sendRueckfrageAnswer(msg, NachrichtTestFactory.REPLY_TO_NACHRICHT_ID);
-
-            verify(nachrichtenRemoteService).sendAnswer(any(ReplyNachricht.class), anyString());
-        }
-
-        @Test
-        void shouldThrowAddressNotFoundException() {
-            doThrow(AddressNotFoundException.class).when(nachrichtEventService).getServiceUrlOfNachricht(anyString());
-
-            assertThatExceptionOfType(AddressNotFoundException.class).isThrownBy(
-                    () -> nachrichtenService.sendRueckfrageAnswer(msg, NachrichtTestFactory.REPLY_TO_NACHRICHT_ID));
-        }
-
-        @Test
-        void shouldThrowIllegalArgumentException() {
-            assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(
-                    () -> nachrichtenService.sendRueckfrageAnswer(mock(ReplyNachricht.class), null));
-        }
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/PostfachMailGrpcService.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/PostfachMailGrpcService.java
deleted file mode 100644
index 9620bb666a4f45a6a4efade642db00690595bf28..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/PostfachMailGrpcService.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.mail.postfach.*;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCommand;
-import io.grpc.stub.StreamObserver;
-import net.devh.boot.grpc.server.service.GrpcService;
-
-@GrpcService
-public class PostfachMailGrpcService extends PostfachServiceGrpc.PostfachServiceImplBase {
-
-    @Override
-    public void findPostfachMail(GrpcFindPostfachMailRequest request, StreamObserver<GrpcFindPostfachMailResponse> responseObserver) {
-        responseObserver.onNext(createResponse(request));
-        responseObserver.onCompleted();
-    }
-
-    private GrpcFindPostfachMailResponse createResponse(GrpcFindPostfachMailRequest request) {
-        var postfachMail = GrpcPostfachMailTestFactory.createBuilder().setId(request.getNachrichtId()).build();
-        return GrpcFindPostfachMailResponse.newBuilder()
-                .setNachricht(postfachMail)
-                .build();
-    }
-
-    @Override
-    public void saveNachrichtDraft(GrpcSaveNachrichtDraftRequest request, StreamObserver<GrpcSaveNachrichtDrafResponse> responseObserver) {
-        responseObserver.onNext(GrpcSaveNachrichtDrafResponse.getDefaultInstance());
-        responseObserver.onCompleted();
-    }
-
-    @Override
-    public void sendPostfachMail(GrpcSendPostfachMailRequest request, StreamObserver<GrpcSendPostfachMailResponse> responseObserver) {
-        responseObserver.onNext(GrpcSendPostfachMailResponse.getDefaultInstance());
-        responseObserver.onCompleted();
-    }
-
-    @Override
-    public void findRueckfragen(GrpcFindRueckfragenRequest request, StreamObserver<GrpcFindRueckfragenResponse> responseObserver) {
-        responseObserver.onNext(GrpcRueckfragenTestFactory.createFindRueckfragenResponse());
-        responseObserver.onCompleted();
-    }
-
-    @Override
-    public void getRueckfrage(GrpcGetRueckfrageRequest request, StreamObserver<GrpcGetRueckfrageResponse> responseObserver) {
-        var res = GrpcRueckfragenTestFactory.create();
-        responseObserver.onNext(GrpcGetRueckfrageResponse.newBuilder().setRueckfrage(res).build());
-        responseObserver.onCompleted();
-    }
-
-    @Override
-    public void sendRueckfrageAnswer(GrpcRueckfrageAnswerRequest request, StreamObserver<GrpcCommand> responseObserver) {
-        responseObserver.onNext(GrpcCommandTestFactory.createBuilder().setStatus(GrpcCommandTestFactory.STATUS_SEND).build());
-        responseObserver.onCompleted();
-    }
-}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/TopicMapperTest.java b/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/TopicMapperTest.java
deleted file mode 100644
index d95b547adc89c2c4deecfb11751d67709d375ed6..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/TopicMapperTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-import java.util.UUID;
-
-import static org.assertj.core.api.Assertions.*;
-
-class TopicMapperTest {
-
-    private List<Nachricht> nachrichten;
-
-    private final String RUECKFRAGE_ID_0 = UUID.randomUUID().toString();
-    private final String RUECKFRAGE_ID_1 = UUID.randomUUID().toString();
-
-    @BeforeEach
-    void init() {
-        nachrichten = List.of(
-                NachrichtTestFactory.createBuilder().id(RUECKFRAGE_ID_0).build(),
-                NachrichtTestFactory.createBuilder().id(RUECKFRAGE_ID_1).build()
-        );
-    }
-
-    @Test
-    void shouldMapVorgangId() {
-        var topicList = TopicMapper.toTopics(nachrichten);
-        assertThat(topicList.get(0).clerkMessage().id()).isEqualTo(RUECKFRAGE_ID_0);
-    }
-
-    @Test
-    void shouldHaveTopics() {
-        var topics = TopicMapper.toTopics(nachrichten);
-        assertThat(topics).hasSize(2);
-    }
-
-    @Test
-    void shouldHaveClerkNachricht() {
-        var topics = TopicMapper.toTopics(nachrichten);
-        assertThat(topics.get(0).clerkMessage()).isNotNull();
-    }
-
-    @Test
-    void shouldNotHaveReplyInClerkNachricht() {
-        var topics = TopicMapper.toTopics(nachrichten);
-        assertThat(topics.get(0).clerkMessage().replyNachrichten()).isNull();
-    }
-
-    @Test
-    void shouldHaveUserNachrichten() {
-        var topics = TopicMapper.toTopics(nachrichten);
-
-        Topic topic0 = topics.get(0);
-        assertThat(topic0.userMessages()).hasSize(1);
-    }
-}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/resources/application.properties b/ozg-antragsraum-server/src/test/resources/application.properties
deleted file mode 100644
index 7317357c62d17c12a8f054b6087b9bf4e257b61c..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/resources/application.properties
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# 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.
-#
-spring.application.name=Antragsraum
-spring.mvc.pathmatch.matching-strategy=ant_path_matcher
-spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
-spring.servlet.multipart.max-file-size=150MB
-spring.servlet.multipart.max-request-size=2GB
-management.server.port=8081
-spring.data.mongodb.database=InformationManager
-spring.data.mongodb.port=${mongodb.container.port}
-spring.data.mongodb.host=localhost
-spring.data.mongodb.auto-index-creation=true
-grpc.client.info_manager.address=in-process:test
-grpc.client.info_manager.negotiation-type=PLAINTEXT
-ozgcloud.grpc.client.negotiation-type=PLAINTEXT
-logging.level.org.springframework.security=DEBUG
-logging.level.org.springframework.web.client.RestTemplate=DEBUG
-# ClamAV properties
-clamav.server.host=localhost
-clamav.server.port=3310
diff --git a/ozg-antragsraum-server/src/test/resources/user.json b/ozg-antragsraum-server/src/test/resources/user.json
deleted file mode 100644
index 01f2c01cb71623d8bd06009868590522888e964c..0000000000000000000000000000000000000000
--- a/ozg-antragsraum-server/src/test/resources/user.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "iss": "https://localhost/realms/antragsraum",
-  "sub": "281c4558-550c-413b-9972-2d2e5bde6b9b",
-  "iat": 1695992542,
-  "exp": 1695992642,
-  "preferred_username": "janedoe",
-  "realm_access": {
-    "roles": [
-      "admin",
-      "ROLE_AUTHORIZED_PERSONNEL"
-    ]
-  },
-  "email": "jane.doe@ozg-cloud.com",
-  "scope": "openid email"
-}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e5c4ee653a9ae8dd6834438a69dee1f94fcba281
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>de.ozgcloud</groupId>
+    <artifactId>antragsraum</artifactId>
+    <version>1.0.0</version>
+
+    <name>OZG-Cloud Antragsraum</name>
+    <packaging>pom</packaging>
+
+    <properties>
+        <ozgcloud.license.version>1.6.0</ozgcloud.license.version>
+    </properties>
+
+    <modules>
+        <module>server</module>
+    </modules>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>com.mycila</groupId>
+                    <artifactId>license-maven-plugin</artifactId>
+                    <version>4.1</version>
+                    <configuration>
+                        <mapping>
+                            <config>SCRIPT_STYLE</config>
+                        </mapping>
+                        <licenseSets>
+                            <licenseSet>
+                                <header>license/eupl_v1_2_de/header.txt</header>
+                                <excludes>
+                                    <exclude>**/README</exclude>
+                                    <exclude>src/test/resources/**</exclude>
+                                    <exclude>src/main/resources/**</exclude>
+                                </excludes>
+                            </licenseSet>
+                        </licenseSets>
+                    </configuration>
+                    <dependencies>
+                        <dependency>
+                            <groupId>de.ozgcloud.common</groupId>
+                            <artifactId>ozgcloud-common-license</artifactId>
+                            <version>${ozgcloud.license.version}</version>
+                        </dependency>
+                    </dependencies>
+                </plugin>
+                <plugin>
+                    <groupId>org.sonarsource.scanner.maven</groupId>
+                    <artifactId>sonar-maven-plugin</artifactId>
+                </plugin>
+                <plugin>
+                    <groupId>org.jacoco</groupId>
+                    <artifactId>jacoco-maven-plugin</artifactId>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <distributionManagement>
+        <repository>
+            <id>ozg-nexus</id>
+            <name>ozg-releases</name>
+            <url>https://nexus.ozg-sh.de/repository/ozg-releases/</url>
+        </repository>
+        <snapshotRepository>
+            <id>ozg-snapshots-nexus</id>
+            <name>ozg-snapshots</name>
+            <url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url>
+        </snapshotRepository>
+    </distributionManagement>
+</project>
diff --git a/release-erstellen.sh b/release-erstellen.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fc5a1f481ba0e2d029766c45d2551321d93495f4
--- /dev/null
+++ b/release-erstellen.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+if [ "$#" -ne 1 ]; then
+    echo "Aufruf: release-erstellen.sh JA"
+    echo "Als Parameter bitte 'JA' eintragen zur Sicherheit"
+    exit 1
+fi
+
+
+## alle -SNAPSHOT in pom.xmls entfernen
+SED_PARAMS="-i"
+if [[ "$OSTYPE" =~ ^darwin ]]; then
+    SED_PARAMS="$SED_PARAMS '' -e"
+fi
+find . -name pom.xml -exec sed $SED_PARAMS 's/-SNAPSHOT//g' {} +
+
+## release version auslesen
+NEWVERSION=$(xmlstarlet sel -N w="http://maven.apache.org/POM/4.0.0" -t -v '//w:project/w:version' -n pom.xml)
+
+echo
+echo "NEXT STEPS:"
+echo "***********"
+echo "Änderungen prüfen"
+echo "git commit -a -m 'release version "$NEWVERSION"'"
+echo "git push"
+echo "git tag "$NEWVERSION
+echo "git push --tags"
diff --git a/release-startdev.sh b/release-startdev.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6b79073651802cb6b2f06355bdfca08f511e2130
--- /dev/null
+++ b/release-startdev.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+#set -x
+
+if [ "$#" -ne 1 ]; then
+    echo "Aufruf: ozg-release-startdev.sh NEWVERSION"
+    exit 1
+fi
+
+NEWVERSION=$1
+
+echo
+
+# pom.xml:main -> project.version setzen
+# projectname/pom.xml:parent -> project.parent.version setzen
+# projectname/pom.xml:parent,main -> project.parent.version und project.version setzen
+#
+PROJECTS="pom.xml:main 
+          vorgang-manager-server/pom.xml:main
+          vorgang-manager-interface/pom.xml:main
+          vorgang-manager-utils/pom.xml:main
+          nachrichten-manager/pom.xml:main
+          notification-manager/pom.xml:main
+          vorgang-manager-command/pom.xml:main
+          vorgang-manager-base/pom.xml:main
+          bescheid-manger/pom.xml:main
+        "
+
+for PROJECT in $PROJECTS;
+do
+  POMFILE=$(echo $PROJECT | cut -d':' -f1)
+  ACTIONS=$(echo $PROJECT | cut -d':' -f2)
+
+  ## Auf SNAPSHOT Versionen testen
+  if fgrep -q "SNAPSHOT" $POMFILE; then
+    RED='\033[0;31m'
+    NC='\033[0m'
+    echo "${RED}ERROR: Datei "$POMFILE" enthält noch SNAPSHOT Versionen, das sollte hier nicht passieren.${NC}"
+    exit 1
+  fi
+echo $ACTIONS
+  ## Versionen setzen
+  if [[ $ACTIONS == "main" ]] ; then
+    xmlstarlet ed --pf -L -N w="http://maven.apache.org/POM/4.0.0" -u '//w:project/w:version' -v $NEWVERSION $POMFILE
+  fi
+
+  if [[ $ACTIONS == "parent" ]]; then
+    xmlstarlet ed --pf -L -N w="http://maven.apache.org/POM/4.0.0" -u '//w:project/w:parent/w:version' -v $NEWVERSION $POMFILE
+  fi
+done
+
+
+
+echo
+echo "NEXT STEPS:"
+echo "***********"
+echo "Änderungen prüfen"
+echo "git commit -a -m 'start development "$NEWVERSION"'"
+echo "git push"
+
diff --git a/server/pom.xml b/server/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4e4b88a62e65c26dfe112e85ed27819c4853ea94
--- /dev/null
+++ b/server/pom.xml
@@ -0,0 +1,369 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>de.ozgcloud.common</groupId>
+        <artifactId>ozgcloud-common-parent</artifactId>
+        <version>4.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>de.ozgcloud.antragsraum</groupId>
+    <artifactId>antragsraum-server</artifactId>
+    <version>1.0.0</version>
+
+    <name>OZG-Cloud Antragsraum Server</name>
+    <description>Server Implementierung des Antragsraums</description>
+
+    <properties>
+        <java.version>21</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+        <spring-boot.build-image.imageName>docker.ozg-sh.de/antragsraum-server:build-latest</spring-boot.build-image.imageName>
+
+        <ozgcloud.license.version>1.6.0</ozgcloud.license.version>
+        <ozgcloud-starter.version>0.9.0</ozgcloud-starter.version>
+
+        <maven-deploy-plugin.version>3.0.0</maven-deploy-plugin.version>
+        <find-and-replace-maven-plugin.version>1.1.0</find-and-replace-maven-plugin.version>
+        <docker-java.version>3.3.3</docker-java.version>
+
+        <grpc-test.version>1.19.0</grpc-test.version>
+        <net-devh-grpc.version>2.15.0.RELEASE</net-devh-grpc.version>
+        <grpc.version>1.57.2</grpc.version>
+        <modulith.version>1.1.3</modulith.version>
+        <info-manager-interface.version>1.0.0</info-manager-interface.version>
+        <jjwt.version>0.12.5</jjwt.version>
+        <openapi.version>2.3.0</openapi.version>
+        <nachrichten-manager-interface.version>2.7.0</nachrichten-manager-interface.version>
+    </properties>
+
+    <repositories>
+        <repository>
+            <id>central</id>
+            <name>Maven Central</name>
+            <url>https://repo1.maven.org/maven2/</url>
+        </repository>
+        <repository>
+            <id>nexus</id>
+            <name>Ozg nexus</name>
+            <url>https://nexus.ozg-sh.de/repository/ozg-releases</url>
+        </repository>
+        <repository>
+            <id>shibboleth-releases</id>
+            <name>Shibboleth Releases Repository</name>
+            <url>https://build.shibboleth.net/maven/releases/</url>
+            <releases>
+                <enabled>true</enabled>
+                <checksumPolicy>warn</checksumPolicy>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>shibboleth-thirdparty</id>
+            <name>Shibboleth Thirdparty Repository</name>
+            <url>https://build.shibboleth.net/maven/thirdparty/</url>
+            <releases>
+                <enabled>true</enabled>
+                <checksumPolicy>fail</checksumPolicy>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>de.ozgcloud.info</groupId>
+            <artifactId>info-manager-interface</artifactId>
+            <version>${info-manager-interface.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>de.ozgcloud.nachrichten</groupId>
+            <artifactId>nachrichten-manager-interface</artifactId>
+            <version>${nachrichten-manager-interface.version}</version>
+        </dependency>
+
+        <!-- Spring -->
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-client-spring-boot-starter</artifactId>
+            <version>${net-devh-grpc.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>net.devh</groupId>
+            <artifactId>grpc-server-spring-boot-starter</artifactId>
+            <version>${net-devh-grpc.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-saml2-service-provider</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.hateoas</groupId>
+            <artifactId>spring-hateoas</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.modulith</groupId>
+            <artifactId>spring-modulith-starter-core</artifactId>
+            <version>${modulith.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.modulith</groupId>
+            <artifactId>spring-modulith-docs</artifactId>
+            <version>${modulith.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+            <version>${openapi.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+
+        <!-- aspectJ -->
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjweaver</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.aspectj</groupId>
+            <artifactId>aspectjrt</artifactId>
+        </dependency>
+
+        <!-- Tools -->
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-collections4</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.thedeanda</groupId>
+            <artifactId>lorem</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Dev -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <scope>runtime</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>de.ozgcloud.api-lib</groupId>
+            <artifactId>ozg-cloud-spring-boot-starter</artifactId>
+            <version>${ozgcloud-starter.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava-testlib</artifactId>
+            <version>31.1-jre</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>de.ozgcloud.antragsraum.AntragsRaumApplication</mainClass>
+                    <image>
+                        <!-- cann be removed when common-lib > 2.3.2-->
+                        <builder>paketobuildpacks/builder-jammy-base</builder>
+                        <env>
+                            <BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS>
+                            <BPE_APPEND_JAVA_TOOL_OPTIONS>-Dfile.encoding=UTF-8</BPE_APPEND_JAVA_TOOL_OPTIONS>
+                        </env>
+                    </image>
+                    <profiles>
+                        <profile>local</profile>
+                        <profile>a12proc</profile>
+                    </profiles>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>pl.project13.maven</groupId>
+                <artifactId>git-commit-id-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>com.mycila</groupId>
+                <artifactId>license-maven-plugin</artifactId>
+                <configuration>
+                    <mapping>
+                        <config>SCRIPT_STYLE</config>
+                        <ftlh>FTL</ftlh>
+                    </mapping>
+                    <licenseSets>
+                        <licenseSet>
+                            <header>license/eupl_v1_2_de/header.txt</header>
+                            <excludes>
+                                <exclude>**/README</exclude>
+                                <exclude>src/test/resources/**</exclude>
+                                <exclude>src/main/resources/*.yml</exclude>
+                                <exclude>src/main/resources/*.txt</exclude>
+                            </excludes>
+                        </licenseSet>
+                    </licenseSets>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>de.ozgcloud.common</groupId>
+                        <artifactId>ozgcloud-common-license</artifactId>
+                        <version>${ozgcloud.license.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
+    <distributionManagement>
+        <repository>
+            <id>ozg-nexus</id>
+            <name>ozg-releases</name>
+            <url>https://nexus.ozg-sh.de/repository/ozg-releases/</url>
+        </repository>
+        <snapshotRepository>
+            <id>ozg-snapshots-nexus</id>
+            <name>ozg-snapshots</name>
+            <url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url>
+        </snapshotRepository>
+    </distributionManagement>
+
+</project>
diff --git a/server/run_helm_test.sh b/server/run_helm_test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..14fa4e627faa23cb318e0e654f08f12548f7aafe
--- /dev/null
+++ b/server/run_helm_test.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+helm template  src/main/helm/ -f src/test/helm-linter-values.yaml
+helm lint -f src/test/helm-linter-values.yaml src/main/helm/
+cd src/main/helm && helm unittest  -f '../../test/helm/**/*test.yaml' .
diff --git a/server/src/main/docker/docker-compose-full.yml b/server/src/main/docker/docker-compose-full.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f63f97f4046b1f2485d344493a635ec335d15afd
--- /dev/null
+++ b/server/src/main/docker/docker-compose-full.yml
@@ -0,0 +1,98 @@
+version: '3'
+volumes:
+  mongodb-data:
+services:
+  ozg-mongodb:
+    image: mongo:4
+    ports:
+      - "27017:27017"
+    environment:
+      - MONGODB_EXTRA_FLAGS=--profile=2
+    volumes:
+      - mongodb-data:/data/db
+  ozg-clamav:
+    image: 'docker.ozg-sh.de/clamav/clamav:stable'
+    ports:
+      - "3310:3310"
+  ozg-clamav-api:
+    image: benzino77/clamav-rest-api
+    command: ['/usr/bin/wait-for-it', '-h', 'ozg-clamav', '-p', '3310', '-s', '-t', '60', '--', 'npm', 'start']
+    depends_on:
+      - ozg-clamav
+    environment:
+      - NODE_ENV=production
+      - CLAMD_IP=ozg-clamav
+      - APP_FORM_KEY=FILES
+      - APP_PORT=3000
+      - APP_MAX_FILE_SIZE=26214400
+    ports:
+      - "3000:3000"
+  ozg-info-manager:
+    image: docker.ozg-sh.de/info-manager:OZG-4804-0.1.0-SNAPSHOT
+    platform: linux/amd64
+    ports:
+      - "9091:9090"
+      - "8084:8081"
+    environment:
+      - SPRING_DATA_MONGODB_HOST=ozg-mongodb
+      - SPRING_PROFILES_ACTIVE=dev
+  elastic:
+    image: docker.elastic.co/elasticsearch/elasticsearch:8.3.2
+    ports:
+      - "9200:9200"
+      - "9300:9300"
+    environment:
+      - discovery.type=single-node
+      - ELASTIC_PASSWORD=password
+      - ES_JAVA_OPTS=-Xms1g -Xmx1g
+      - MEM_LIMIT=1073741824
+      - xpack.security.enabled=false
+      - xpack.security.http.ssl.enabled=false
+    ulimits:
+      memlock:
+        soft: -1
+        hard: -1
+      nofile:
+        soft: 65536
+        hard: 65536
+    healthcheck:
+      test:
+        [
+          'CMD-SHELL',
+          "curl -s 'http://localhost:9200/_cat/health?h=status' | egrep -q '(green|yellow)'",
+        ]
+      interval: 10s
+      timeout: 10s
+      retries: 5
+  user-manager:
+    image: docker.ozg-sh.de/user-manager:${USER_MANAGER_DOCKER_IMAGE:-snapshot-latest}
+    platform: linux/amd64
+    environment:
+      - KEYCLOAK_URL=https://sso.dev.by.ozg-cloud.de
+      - OZGCLOUD_KEYCLOAK_API_CLIENT=alfa
+      - OZGCLOUD_KEYCLOAK_API_PASSWORD= 
+      - OZGCLOUD_KEYCLOAK_API_REALM=${KEYCLOAK_REALM:-by-e2e-local-dev}
+      - OZGCLOUD_KEYCLOAK_API_USER=usermanagerapiuser
+      - OZGCLOUD_USER_MANAGER_URL=http://localhost:9092
+      - OZGCLOUD_USERSYNC_PERIOD=disabled
+      - QUARKUS_GRPC_SERVER_SSL_CERTIFICATE=
+      - QUARKUS_GRPC_SERVER_SSL_KEY=
+      - QUARKUS_HTTP_CORS_ORIGINS=http://localhost:4300,http://127.0.0.1:4300,https://e2e.dev.by.ozg-cloud.de,http://localhost:8080
+      - QUARKUS_LOG_CONSOLE_JSON=false
+      - QUARKUS_MONGODB_CONNECTION_STRING=mongodb://mongodb:27017
+      - QUARKUS_MONGODB_DATABASE=usermanager
+      - QUARKUS_OIDC_AUTH_SERVER_URL=https://sso.dev.by.ozg-cloud.de/realms/${KEYCLOAK_REALM:-by-e2e-local-dev}
+      - QUARKUS_OIDC_CLIENT_ID=alfa
+      - quarkus.log.category."io.quarkus.oidc.runtime.OidcProvider".level=TRACE
+      - quarkus.log.category."io.quarkus.oidc.runtime.OidcProvider".min-level=TRACE
+    ports:
+      - "9093:8080"
+      - "9000:9000"
+    depends_on:
+      - ozg-mongodb
+
+  smocker:
+    image: thiht/smocker
+    ports:
+      - "7080:8080"
+      - "7081:8081"
\ No newline at end of file
diff --git a/server/src/main/docker/docker-compose.yml b/server/src/main/docker/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..af01dcf3a7fe3db05d28826380b2d0de2064ffc3
--- /dev/null
+++ b/server/src/main/docker/docker-compose.yml
@@ -0,0 +1,37 @@
+version: '3'
+volumes:
+  mongodb-data:
+services:
+  ozg-mongodb:
+    image: mongo:4
+    ports:
+      - "27017:27017"
+    environment:
+      - MONGODB_EXTRA_FLAGS=--profile=2
+    volumes:
+      - mongodb-data:/data/db
+  ozg-clamav:
+    image: 'docker.ozg-sh.de/clamav/clamav:stable'
+    ports:
+      - "3310:3310"
+  ozg-clamav-api:
+    image: benzino77/clamav-rest-api
+    depends_on:
+      - ozg-clamav
+    environment:
+      - NODE_ENV=production
+      - CLAMD_IP=ozg-clamav
+      - APP_FORM_KEY=FILES
+      - APP_PORT=3000
+      - APP_MAX_FILE_SIZE=26214400
+    ports:
+      - "3000:3000"
+  ozg-info-manager:
+    image: docker.ozg-sh.de/info-manager:OZG-4804-0.1.0-SNAPSHOT
+    platform: linux/amd64
+    ports:
+      - "9091:9090"
+      - "8084:8081"
+    environment:
+      - SPRING_DATA_MONGODB_HOST=ozg-mongodb
+      - SPRING_PROFILES_ACTIVE=dev
\ No newline at end of file
diff --git a/gradle.properties b/server/src/main/helm/Chart.yaml
similarity index 51%
rename from gradle.properties
rename to server/src/main/helm/Chart.yaml
index 0fc93ce8e7f7a4fdea18e6d8d95b065a7cf980b5..52162d593ad8fc50b28e78628fd1ca6287645502 100644
--- a/gradle.properties
+++ b/server/src/main/helm/Chart.yaml
@@ -1,7 +1,8 @@
 #
-# 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
+# Copyright (C) 2022 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 -
@@ -20,17 +21,10 @@
 # Die sprachspezifischen Genehmigungen und Beschränkungen
 # unter der Lizenz sind dem Lizenztext zu entnehmen.
 #
-group=com.mgmtp.bup.ozg
-docker_remote_plugin=9.3.3
-semver_plugin=3.0.0
-sonarqube_plugin=4.3.1.3277
-helm_plugin=2.0.0
-# Deployment
-dockerRegistryForPublish=docker.ozg-sh.de
-dockerRegistryPull=docker.ozg-sh.de
-helmRepositoryHost=https://nexus.ozg-sh.de
-helmRepositorySnapshot=ozg-base-apps-snapshot
-helmRepositoryRelease=ozg-base-apps
-namespace=bup-ozg-ci
-appName=ozg-antragsraum
-mavenReleasesRepository=https://artifacts.mgm-tp.com/artifactory/bup-maven-releases/
+
+apiVersion: v1
+appVersion: "1.0"
+description: A Helm chart for Antragsraum-Server
+name: antragsraum-server
+version: 0.0.0-MANAGED-BY-JENKINS
+icon: https://simpleicons.org/icons/helm.svg
diff --git a/server/src/main/helm/templates/NOTES.txt b/server/src/main/helm/templates/NOTES.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/server/src/main/helm/templates/_helpers.tpl b/server/src/main/helm/templates/_helpers.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..a67db66874bbcab57dfc10566b0c14c9a3a1bf6f
--- /dev/null
+++ b/server/src/main/helm/templates/_helpers.tpl
@@ -0,0 +1,80 @@
+
+{{/* error check 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec) */}}
+{{/* Namespace */}}
+{{- define "app.namespace" -}}
+{{- if gt (len (.Release.Namespace)) 63 -}}
+{{- fail (printf ".Release.Namespace %s ist zu lang (max. 63 Zeichen)" .Release.Namespace) -}}
+{{- end -}}
+{{ printf "%s" .Release.Namespace }}
+{{- end -}}
+
+{{/* Chart: Name + Version */}}
+{{- define "app.chart" -}}
+{{- if gt (len (printf "%s-%s" .Chart.Name .Chart.Version)) 63 -}}
+{{- fail (printf ".Chart.Name-.Chart.Version %s-%s ist zu lang (max. 63 Zeichen)" .Chart.Name .Chart.Version) -}}
+{{- end -}}
+{{ printf "%s-%s" .Chart.Name .Chart.Version }}
+{{- end -}}
+
+{{/* Managed-by -> On Helm, this value is always Helm */}}
+{{- define "app.managedBy" -}}
+{{- if gt (len (.Release.Service)) 63 -}}
+{{- fail (printf ".Release.Service %s ist zu lang (max. 63 Zeichen)" .Release.Service) -}}
+{{- end -}}
+{{ printf "%s" .Release.Service }}
+{{- end -}}
+
+
+{{/* Default Labels: Helm recommended best-practice labels https://helm.sh/docs/chart_best_practices/labels/ */}}
+{{- define "app.defaultLabels" }}
+app.kubernetes.io/instance: antragsraum-server
+app.kubernetes.io/managed-by: {{ include "app.managedBy" . }}
+app.kubernetes.io/name: {{ .Release.Name }}
+app.kubernetes.io/namespace: {{ include "app.namespace" . }}
+app.kubernetes.io/part-of: ozgcloud
+app.kubernetes.io/version: {{ .Chart.Version }}
+helm.sh/chart: {{ include "app.chart" . }}
+{{- end -}}
+
+{{- define "app.matchLabels" }}
+app.kubernetes.io/name: {{ .Release.Name }}
+app.kubernetes.io/namespace: {{ include "app.namespace" . }}
+{{- end -}}
+
+{{- define "app.serviceAccountName" -}}
+{{ printf "%s" ( (.Values.serviceAccount).name | default "antragsraum-server-service-account" ) }}
+{{- end -}}
+
+{{- define "app.baseDomain" -}}
+{{- required "baseUrl muss angegeben sein" .Values.baseUrl }}
+{{- end -}}
+
+{{- define "app.envSpringProfiles" }}
+{{- if (.Values.env).overrideSpringProfiles -}}
+{{ printf "%s" (.Values.env).overrideSpringProfiles }}
+{{- else -}}
+{{ printf "oc, %s" (include "app.ozgcloudEnvironment" . ) }}
+{{- end -}}
+{{- end -}}
+
+{{- define "app.ozgcloudEnvironment" -}}
+{{- required "ozgcloud.environment muss angegeben sein" (.Values.ozgcloud).environment -}}
+{{- end -}}
+
+{{- define "app.getCustomList" -}}
+{{- with (.Values.env).customList -}}
+{{- if kindIs "map" . -}}
+{{ include "app.dictToList" . }}
+{{- else if kindIs "slice" . -}}
+{{ . | toYaml }}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "app.dictToList" -}}
+{{- $customList := list -}}
+{{- range $key, $value := . -}}
+{{- $customList = append $customList (dict "name" $key "value" $value) }}
+{{- end -}}
+{{- $customList | toYaml -}}
+{{- end -}}
\ No newline at end of file
diff --git a/server/src/main/helm/templates/create_jwt_secret_network_policy.yaml b/server/src/main/helm/templates/create_jwt_secret_network_policy.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4c2f801a7f8f463da30eacc1d31a9e937ae6d548
--- /dev/null
+++ b/server/src/main/helm/templates/create_jwt_secret_network_policy.yaml
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2022 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.
+#
+{{- if not (.Values.networkPolicy).disabled }} 
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: network-policy-create-antragsraum-jwt-secret
+  namespace: {{ .Release.Namespace }} 
+spec:
+  podSelector:
+    matchLabels:
+      component: create-antragsraum-jwt-secret
+  policyTypes:
+  - Egress
+  egress:
+  # kube api
+  - to:
+    - ipBlock: 
+        cidr: 172.18.0.1/32
+    - ipBlock: 
+        cidr: 10.0.0.0/8
+    ports:
+    - port: 443
+    - port: 6443
+{{- end }} 
\ No newline at end of file
diff --git a/server/src/main/helm/templates/create_jwt_secret_rbac.yaml b/server/src/main/helm/templates/create_jwt_secret_rbac.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a211f59093e3164d15718982a67ebbf75946093e
--- /dev/null
+++ b/server/src/main/helm/templates/create_jwt_secret_rbac.yaml
@@ -0,0 +1,57 @@
+#
+# Copyright (C) 2022 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.
+#
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: create-antragsraum-jwt-secret-service-account
+  namespace: {{ .Release.Namespace }}
+---
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: create-antragsraum-jwt-secret-role-binding
+  namespace: {{ .Release.Namespace }}
+subjects:
+  - kind: ServiceAccount
+    name: create-antragsraum-jwt-secret-service-account
+roleRef:
+  kind: Role
+  name: create-antragsraum-jwt-secret-role
+  apiGroup: rbac.authorization.k8s.io
+---
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: create-antragsraum-jwt-secret-role
+  namespace: {{ .Release.Namespace }}
+rules:
+  - apiGroups:
+      - ""
+    resources:
+      - secrets
+    verbs:
+      - get
+      - list
+      - create
diff --git a/server/src/main/helm/templates/create_jwt_secret_script_configmap.yaml b/server/src/main/helm/templates/create_jwt_secret_script_configmap.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e6cfce626e2af3217cd2f18a708e1fe634a27ff0
--- /dev/null
+++ b/server/src/main/helm/templates/create_jwt_secret_script_configmap.yaml
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: create-antragsraum-jwt-secret-script
+  namespace: {{ .Release.Namespace }}
+data:
+  create-antragsraum-jwt-secret.sh: |
+    #!/bin/sh
+    
+    kubectl get secrets -n {{ .Release.Namespace }} -o json >> /tmp/secrets.json
+    
+    jq '.items[] | .metadata.name' /tmp/secrets.json
+  
+    secretName="antragsraum-jwt-secret"
+
+    antragsraumJWTSecretExists=$(jq -e ".items[] | select(.metadata.name == \"$secretName\")" /tmp/secrets.json > /dev/null ; echo "$?")
+   
+    echo "$secretName status: $antragsraumJWTSecretExists"
+
+    if [ "$antragsraumJWTSecretExists" -ne 0 ]
+    then
+      echo "Erstelle neues Secret: $secretName"
+      kubectl create secret generic $secretName --namespace={{ .Release.Namespace }} --from-literal="jwt-secret=$(openssl rand -base64 72)"
+    else
+      echo "Kein neues Secret für antragsraum jwt"
+    fi
+  
\ No newline at end of file
diff --git a/server/src/main/helm/templates/create_secret_pod.yaml b/server/src/main/helm/templates/create_secret_pod.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1ff83e7a41b0c09c2249017523b417e3ed5183bc
--- /dev/null
+++ b/server/src/main/helm/templates/create_secret_pod.yaml
@@ -0,0 +1,50 @@
+#
+# Copyright (C) 2022 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.
+#
+
+apiVersion: v1
+kind: Pod
+metadata:
+  name: create-antragsraum-jwt-secret
+  namespace: {{ .Release.Namespace }}
+  labels:
+    component: create-antragsraum-jwt-secret
+spec:
+  serviceAccountName: create-antragsraum-jwt-secret-service-account
+  restartPolicy: Never
+  containers:
+    - name: create-antragsraum-jwt-secret
+      image: "{{ .Values.image.kubectl.repo }}/{{ .Values.image.kubectl.name }}:{{ .Values.image.kubectl.tag }}"
+      command: ["/bin/sh", "-c", "./script/create-antragsraum-jwt-secret.sh"]
+      volumeMounts:
+        - mountPath: "/script"
+          name: create-antragsraum-jwt-secret-volume
+  volumes:
+    - name: create-antragsraum-jwt-secret-volume
+      configMap:
+        defaultMode: 493
+        name: create-antragsraum-jwt-secret-script
+{{- if .Values.imagePullSecret }}
+  imagePullSecrets:
+  - name: {{ .Values.imagePullSecret }}
+{{- end }}
\ No newline at end of file
diff --git a/server/src/main/helm/templates/deployment.yaml b/server/src/main/helm/templates/deployment.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3b6292db32e239982a17f3d5839971ff0f824335
--- /dev/null
+++ b/server/src/main/helm/templates/deployment.yaml
@@ -0,0 +1,195 @@
+#
+# 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.
+#
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ .Release.Name }}
+  namespace: {{ include "app.namespace" . }}
+  labels:
+    {{- include "app.defaultLabels" . | indent 4 }}
+spec:
+  progressDeadlineSeconds: 600
+  revisionHistoryLimit: 10
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      {{- include "app.matchLabels" . | indent 6 }}
+  strategy:
+    rollingUpdate:
+      maxSurge: 1
+      maxUnavailable: 0
+    type: RollingUpdate
+  template:
+    metadata:
+      labels:
+        {{- include "app.defaultLabels" . | indent 8 }}
+        component: antragsraum-server
+    spec:
+      {{- if (.Values.serviceAccount).create }}
+      serviceAccountName: {{ include "app.serviceAccountName" . }}
+      {{- end }}
+      
+      topologySpreadConstraints:
+      - maxSkew: 1
+        topologyKey: kubernetes.io/hostname
+        whenUnsatisfiable: ScheduleAnyway
+        labelSelector:
+          matchLabels:
+            app.kubernetes.io/name: {{ .Release.Name }}
+
+      imagePullSecrets:
+        - name:  {{ required "imagePullSecret must be set" .Values.imagePullSecret }}
+      restartPolicy: Always
+      containers:
+      - name: antragsraum-server
+        image: "{{ .Values.image.repo }}/{{ .Values.image.name }}:{{ coalesce (.Values.image).tag "latest" }}"
+        imagePullPolicy: Always
+        ports:
+          - containerPort: 8080
+            name: 8080tcp1
+            protocol: TCP
+          - containerPort: 8081
+            name: metrics
+            protocol: TCP
+        readinessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /actuator/health/readiness
+            port: 8081
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          initialDelaySeconds: 120
+          timeoutSeconds: 3
+        startupProbe:
+          httpGet:
+            path: /actuator/health/readiness
+            port: 8081
+            scheme: HTTP
+          failureThreshold: 10
+          initialDelaySeconds: 120
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 5
+        env:
+        - name: spring_profiles_active
+          value: {{ include "app.envSpringProfiles" . }}
+
+      
+        - name: OZGCLOUD_ANTRAGSRAUM_LOGOUTSUCCESSURL
+          value: {{ required ".Values.antragsraum.logoutSuccessUrl must be set" (.Values.antragsraum).logoutSuccessUrl }}
+
+
+        - name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_SIGNING_CREDENTIALS_0_PRIVATE-KEY-LOCATION
+          value: file:///keystore/signing.key
+        - name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_SIGNING_CREDENTIALS_0_CERTIFICATE-LOCATION
+          value: file:///keystore/signing.crt
+        - name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_DECRYPTION_CREDENTIALS_0_PRIVATE-KEY-LOCATION
+          value: file:///keystore/enc.key
+
+        - name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_DECRYPTION_CREDENTIALS_0_CERTIFICATE-LOCATION
+          value: file:///keystore/enc.crt
+        
+        - name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_ASSERTINGPARTY_METADATA-URI
+          value: file:///metadata/bayernid-idp-infra.xml
+
+        - name: OZGCLOUD_JWT_SECRET
+          valueFrom:
+            secretKeyRef:
+             name: antragsraum-jwt-secret
+             key: jwt-secret
+             optional: false
+
+        - name: GRPC_CLIENT_INFO-MANAGER_NEGOTIATIONTYPE
+          value: {{ required ".Values.grpcclient.infomanager.negotiationtype must be set" .Values.grpcclient.infomanager.negotiationtype }}
+        
+        - name: GRPC_CLIENT_INFO-MANAGER_ADDRESS
+          value: {{ required ".Values.grpcclient.infomanager.address must be set" .Values.grpcclient.infomanager.address }}
+
+        - name: CLAMAV_SCANURL
+          value: {{ required ".Values.clamav.scanUrl must be set"  .Values.clamav.scanUrl }}
+
+        {{- with include "app.getCustomList" . }}
+{{ . | indent 8 }}
+        {{- end }}
+        resources:
+        {{- with .Values.resources }}
+{{ toYaml . | indent 10 }}
+        {{- end }}
+        securityContext:
+          allowPrivilegeEscalation: false
+          privileged: false
+          readOnlyRootFilesystem: false
+          runAsNonRoot: true
+          {{- with (.Values.securityContext).runAsUser }}
+          runAsUser: {{ . }}
+          {{- end }}
+          {{- with (.Values.securityContext).runAsGroup }}
+          runAsGroup: {{ . }}
+          {{- end }}
+        stdin: true
+        terminationMessagePath: /dev/termination-log
+        terminationMessagePolicy: File
+        tty: true
+        volumeMounts:
+          - mountPath: /tmp/antragsraum
+            name: tmp-volume
+          - name: saml-mount
+            mountPath: "/keystore/signing.crt"
+            subPath: signing.crt
+            readOnly: true
+          - name: saml-mount
+            mountPath: "/keystore/enc.crt"
+            subPath: enc.crt
+            readOnly: true
+          - name: saml-mount
+            mountPath: "/keystore/enc.key"
+            subPath: enc.key
+            readOnly: true
+          - name: saml-mount
+            mountPath: "/keystore/signing.key"
+            subPath: signing.key
+            readOnly: true
+          - name: saml-mount
+            mountPath: "/metadata/bayernid-idp-infra.xml"
+            subPath: metadata.xml
+            readOnly: true
+
+      volumes:
+        - name: tmp-volume
+          emptyDir: { }
+        - name: saml-mount
+          secret:
+            secretName: {{ required ".Values.samlRegistrationSecretName must be set" .Values.samlRegistrationSecretName }}
+            optional: false
+  
+      dnsConfig: {}
+      dnsPolicy: ClusterFirst      
+      {{- with .Values.hostAliases }}
+      hostAliases:
+{{ toYaml . | indent 8 }}
+      {{- end }}
+      schedulerName: default-scheduler
+      securityContext: {}
+      terminationGracePeriodSeconds: 30
\ No newline at end of file
diff --git a/server/src/main/helm/templates/network_policy.yaml b/server/src/main/helm/templates/network_policy.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b583a41228819e8ec806f09afaa2e3980d8f18e1
--- /dev/null
+++ b/server/src/main/helm/templates/network_policy.yaml
@@ -0,0 +1,84 @@
+#
+# 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.
+#
+{{- if not (.Values.networkPolicy).disabled }}
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: network-policy-antragsraum-server
+  namespace: {{ .Release.Namespace }}
+spec:
+  podSelector:
+    matchLabels:
+      component: antragsraum-server
+  policyTypes:
+    - Ingress
+    - Egress
+  ingress:
+  - ports:
+    - port: 8080         
+{{- with (.Values.networkPolicy).additionalIngressConfigLocal }}
+{{ toYaml . | indent 2 }}
+{{- end }}
+{{- with (.Values.networkPolicy).additionalIngressConfigGlobal }}
+{{ toYaml . | indent 2 }}
+{{- end }}
+  egress:
+    - to:
+      - podSelector: 
+          matchLabels:
+            component: info-manager
+      ports:
+        - port: 9090
+          protocol: TCP
+    - to:
+      - namespaceSelector: {}
+        podSelector:
+          matchLabels:
+            app.kubernetes.io/instance: vorgang-manager
+      ports:
+        - port: 9090
+          protocol: TCP
+        
+    - to:
+      - namespaceSelector:
+          matchLabels:
+            kubernetes.io/metadata.name: {{ required "networkPolicy.dnsServerNamespace must be set" (.Values.networkPolicy).dnsServerNamespace }}
+      ports:
+        - port: 53
+          protocol: UDP
+        - port: 53
+          protocol: TCP
+        - port: 5353
+          protocol: UDP
+        - port: 5353
+          protocol: TCP
+
+{{- with (.Values.networkPolicy).additionalEgressConfigLocal }}
+{{ toYaml . | indent 4 }}
+{{- end }}
+{{- with (.Values.networkPolicy).additionalEgressConfigGlobal }}
+{{ toYaml . | indent 4 }}
+{{- end }}
+
+{{- end }}
\ No newline at end of file
diff --git a/server/src/main/helm/templates/service.yaml b/server/src/main/helm/templates/service.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ec95038dd62f99ff45e096add565d81e0e086ed1
--- /dev/null
+++ b/server/src/main/helm/templates/service.yaml
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ .Release.Name }}
+  namespace: {{ include "app.namespace" . }}
+  labels:
+    {{- include "app.defaultLabels" . | indent 4 }}
+    component: antragsraum-server-service
+spec:
+  selector:
+    {{- include "app.matchLabels" . | indent 4 }}
+    component: antragsraum-server
+  ports:
+    - name: http
+      protocol: TCP
+      port: 8080
+      targetPort: 8080
+    - name: management
+      protocol: TCP
+      port: 8081
+      targetPort: 8081
diff --git a/server/src/main/helm/values.yaml b/server/src/main/helm/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..cc798b685fbb1485f27ef20b9e2a29190127ba5f
--- /dev/null
+++ b/server/src/main/helm/values.yaml
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+image:
+  repo: docker.ozg-sh.de
+  name: antragsraum-server
+  tag: latest
+  kubectl:
+    repo: dockerproxy.ozg-sh.de
+    name: bitnami/kubectl
+    tag: latest
+replicaCount: 1
+
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/AntragsRaumApplication.java b/server/src/main/java/de/ozgcloud/antragsraum/AntragsRaumApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..42f7f6efdbb6e8176df99fd6d910c4dc44afb9e2
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/AntragsRaumApplication.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2023-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.antragsraum;
+
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder;
+import org.springframework.boot.task.ThreadPoolTaskExecutorCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.security.authentication.AuthenticationTrustResolver;
+import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
+import org.springframework.security.concurrent.DelegatingSecurityContextRunnable;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+@SpringBootApplication
+@ConfigurationPropertiesScan("de.ozgcloud.antragsraum")
+@EnableWebMvc
+@EnableAsync
+@EnableScheduling
+@EnableAspectJAutoProxy(proxyTargetClass = true)
+public class AntragsRaumApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(AntragsRaumApplication.class, args);
+	}
+
+	@Bean
+	AuthenticationTrustResolver trustResolver() {
+		return new AuthenticationTrustResolverImpl();
+	}
+
+	@Bean
+	ThreadPoolTaskExecutorCustomizer addSecurityContextCustomizer() {
+		return customizer -> customizer.setTaskDecorator(DelegatingSecurityContextRunnable::new);
+
+	}
+
+	@Bean
+	ThreadPoolTaskExecutorCustomizer setThreadName() {
+		return customizer -> customizer.setThreadNamePrefix("ozgtask-");
+	}
+
+	@Bean("taskExecutor")
+	@Primary
+	TaskExecutor taskExecutor(ThreadPoolTaskExecutorBuilder builder) {
+		return builder.build();
+	}
+
+	@Bean
+	public CallScope callScope() {
+		return new CallScope();
+	}
+
+	@Bean
+	public static BeanFactoryPostProcessor beanFactoryPostProcessor(CallScope callScope) {
+		return new CallBeanFactoryPostProcessor(callScope);
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/CallBeanFactoryPostProcessor.java b/server/src/main/java/de/ozgcloud/antragsraum/CallBeanFactoryPostProcessor.java
similarity index 88%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/CallBeanFactoryPostProcessor.java
rename to server/src/main/java/de/ozgcloud/antragsraum/CallBeanFactoryPostProcessor.java
index 2756496a15c999d66c3b82f533b117f91745be01..c493630040246c2a71224a9ed80e9c660c1d1a9e 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/CallBeanFactoryPostProcessor.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/CallBeanFactoryPostProcessor.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.BeansException;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/CallScope.java b/server/src/main/java/de/ozgcloud/antragsraum/CallScope.java
similarity index 94%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/CallScope.java
rename to server/src/main/java/de/ozgcloud/antragsraum/CallScope.java
index af151c5958c12fce6907e8b1a3fbc4ed879a0c83..0adc37e5dc830db2dafd500b7bed083fd92f5590 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/CallScope.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/CallScope.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import lombok.NonNull;
 import lombok.extern.log4j.Log4j2;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/OpenApiConfiguration.java b/server/src/main/java/de/ozgcloud/antragsraum/OpenApiConfiguration.java
similarity index 72%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/OpenApiConfiguration.java
rename to server/src/main/java/de/ozgcloud/antragsraum/OpenApiConfiguration.java
index ca3ff07e80048aa852fc9788ecda42a75911459b..db6792078563c84453afc413d8bc05be8dafb53d 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/OpenApiConfiguration.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/OpenApiConfiguration.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,20 +20,17 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import io.swagger.v3.oas.annotations.OpenAPIDefinition;
 import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
 import io.swagger.v3.oas.annotations.info.Info;
-import io.swagger.v3.oas.annotations.security.OAuthFlow;
-import io.swagger.v3.oas.annotations.security.OAuthFlows;
 import io.swagger.v3.oas.annotations.security.SecurityScheme;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 
 @OpenAPIDefinition(info = @Info(title = "Antragsraum",
         description = "Backend for loading messages from the OZG-Cloud backend, sending replies and uploading files", version = "v1"))
-@SecurityScheme(name = "security_auth", type = SecuritySchemeType.OAUTH2,
-        flows = @OAuthFlows(authorizationCode = @OAuthFlow(
-                authorizationUrl = "${springdoc.oAuthFlow.authorizationUrl}", tokenUrl = "${springdoc.oAuthFlow.tokenUrl}")
-        ))
+@SecurityScheme(name = "security_auth", type = SecuritySchemeType.HTTP, bearerFormat = "JWT", scheme = "bearer")
+@ConditionalOnProperty(name = "ozgcloud.mock.auth")
 public class OpenApiConfiguration {
 }
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/Root.java b/server/src/main/java/de/ozgcloud/antragsraum/Root.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ccab0061e9408b70ef24aef63e4e2c716c74f85
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/Root.java
@@ -0,0 +1,31 @@
+/*
+ * 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.antragsraum;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Builder;
+
+import java.time.Instant;
+
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public record Root(String version, Instant buildTime, String javaVersion, String name, boolean isProduction) {
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/RootController.java b/server/src/main/java/de/ozgcloud/antragsraum/RootController.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a9400ec0daf1ac10d78871a65a71afb0af62d02
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/RootController.java
@@ -0,0 +1,52 @@
+/*
+ * 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.antragsraum;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static java.util.Objects.*;
+
+@RestController(RootController.PATH)
+@RequiredArgsConstructor
+public class RootController {
+	private final BuildProperties buildProperties;
+	@Value("${ozgcloud.stage.production:#{true}}")
+	private boolean production = true;
+	static final String PATH = "/";
+
+	@GetMapping
+	public ResponseEntity<Root> getRoot() {
+		var root = new Root.RootBuilder()
+				.version(isNull(buildProperties) ? null : buildProperties.getVersion())
+				.buildTime(isNull(buildProperties) ? null : buildProperties.getTime())
+				.name(isNull(buildProperties) ? null : buildProperties.getName())
+				.javaVersion(System.getProperty("java.version"))
+				.isProduction(production)
+				.build();
+
+		return ResponseEntity.ok(root);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/SecurityConfiguration.java b/server/src/main/java/de/ozgcloud/antragsraum/SecurityConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d54cf4ac3f13e44bd6cc872d2942d791d16b1e2
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/SecurityConfiguration.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2023-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.antragsraum;
+
+import java.util.UUID;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
+import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
+import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
+import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import de.ozgcloud.antragsraum.mocks.MockLogoutSuccessHandler;
+import de.ozgcloud.antragsraum.mocks.MockSuccessHandler;
+import de.ozgcloud.antragsraum.security.AntragsraumLogoutSuccessHandler;
+import de.ozgcloud.antragsraum.security.AntragsraumProperties;
+import de.ozgcloud.antragsraum.security.BayernIdProperties;
+import de.ozgcloud.antragsraum.security.BayernIdSaml2Extension;
+import de.ozgcloud.antragsraum.security.InMemoryUserDetailService;
+import de.ozgcloud.antragsraum.security.JwtTokenFilter;
+import de.ozgcloud.antragsraum.security.SamlUrlAuthenticationSuccessHandler;
+import de.ozgcloud.antragsraum.security.SecurityProvider;
+import de.ozgcloud.antragsraum.security.User;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import net.devh.boot.grpc.server.security.authentication.BasicGrpcAuthenticationReader;
+import net.devh.boot.grpc.server.security.authentication.GrpcAuthenticationReader;
+
+@Configuration
+@EnableWebSecurity
+@RequiredArgsConstructor
+public class SecurityConfiguration {
+	public static final String OPTIONS = "OPTIONS";
+	public static final String GET = "GET";
+	@NonNull
+	private final BayernIdSaml2Extension bayernIdSaml2Extension;
+	@NonNull
+	private final BayernIdProperties bayernIdProperties;
+	@NonNull
+	private final UserDetailsService userDetailsService;
+	@NonNull
+	private final JwtTokenFilter tokenAuthenticationFilter;
+	@NonNull
+	private final AntragsraumProperties properties;
+	@Value("${ozgcloud.mock.auth:false}")
+	private boolean mockAuth;
+	@Value("${ozgcloud.mock.password:}")
+	private String password;
+
+	@Bean
+	public SecurityProvider securityProvider() {
+		return new SecurityProvider();
+	}
+
+	@Bean
+	Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {
+		RelyingPartyRegistrationResolver registrationResolver =
+		  new DefaultRelyingPartyRegistrationResolver(registrations);
+		OpenSaml4AuthenticationRequestResolver authenticationRequestResolver =
+		  new OpenSaml4AuthenticationRequestResolver(registrationResolver);
+		authenticationRequestResolver.setAuthnRequestCustomizer(context -> {
+			context.getAuthnRequest().setForceAuthn(true);
+			context.getAuthnRequest().setExtensions(bayernIdSaml2Extension.createAkdbExtension());
+		});
+		return authenticationRequestResolver;
+	}
+
+	@Bean
+	public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
+	  throws Exception {
+		if (mockAuth) {
+			DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
+			authenticationProvider.setUserDetailsService(userDetailsService);
+			authenticationProvider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
+
+			ProviderManager providerManager = new ProviderManager(authenticationProvider);
+			providerManager.setEraseCredentialsAfterAuthentication(false);
+
+			return providerManager;
+		}
+
+		return authenticationConfiguration.getAuthenticationManager();
+	}
+
+	@Bean
+	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+		http
+		  .authorizeHttpRequests(authorize -> authorize
+			.requestMatchers("/", "/actuator/**", "/swagger-ui/**", "/v3/api-docs/**", "/error", "/favicon.ico",
+			  "/auth/**", "/login", "/api/e2e/**").permitAll()
+			.requestMatchers("/api/**").authenticated()
+			.anyRequest().denyAll())
+		  .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
+		  .csrf(AbstractHttpConfigurer::disable);
+
+		if (mockAuth) {
+			var pwEnc = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+			var user = User.builder().id(UUID.randomUUID().toString()).username("test").password(pwEnc.encode(password)).build();
+			((InMemoryUserDetailService) userDetailsService).addUser(UUID.randomUUID().toString(), user);
+			http.httpBasic(Customizer.withDefaults())
+			  .formLogin(formLoginConfigurer -> formLoginConfigurer.successHandler(
+				new MockSuccessHandler(userDetailsService)))
+			  .logout(logoutConfigurer -> logoutConfigurer.logoutSuccessHandler(new MockLogoutSuccessHandler()));
+		} else {
+			http.saml2Login(
+				saml2LoginConfigurer -> saml2LoginConfigurer.successHandler(getSamlUrlAuthenticationSuccessHandler()))
+			  .saml2Logout(Customizer.withDefaults())
+			  .logout(logoutConfigurer -> logoutConfigurer.logoutSuccessHandler(logoutSuccessHandler()));
+		}
+
+		return http.build();
+	}
+
+	AuthenticationSuccessHandler getSamlUrlAuthenticationSuccessHandler() {
+		return new SamlUrlAuthenticationSuccessHandler(bayernIdProperties.getRedirectUrl(), userDetailsService);
+	}
+
+	AntragsraumLogoutSuccessHandler logoutSuccessHandler() {
+		var handler = new AntragsraumLogoutSuccessHandler(userDetailsService);
+		handler.setDefaultTargetUrl(properties.getLogoutSuccessUrl());
+		return handler;
+	}
+
+	@Bean
+	public WebMvcConfigurer corsConfigurer() {
+		return new WebMvcConfigurer() {
+			@Override
+			public void addCorsMappings(@NonNull CorsRegistry registry) {
+				registry.addMapping("/auth/**").allowedOrigins(properties.getAuthOrigins())
+				  .allowedMethods(GET, OPTIONS, "POST").allowCredentials(true);
+				registry.addMapping("/api/**").allowedOrigins(properties.getApiOrigins())
+				  .allowedMethods(GET, OPTIONS, "PUT", "POST").allowCredentials(true);
+				registry.addMapping("/**").allowedOrigins(properties.getOtherOrigins()).allowedMethods(GET, OPTIONS)
+				  .allowCredentials(true);
+			}
+		};
+	}
+
+	@Bean
+	public GrpcAuthenticationReader grpcAuthenticationReader() {
+		return new BasicGrpcAuthenticationReader();
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/SwaggerCorsConfiguration.java b/server/src/main/java/de/ozgcloud/antragsraum/SwaggerCorsConfiguration.java
similarity index 89%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/SwaggerCorsConfiguration.java
rename to server/src/main/java/de/ozgcloud/antragsraum/SwaggerCorsConfiguration.java
index f05ea7622856b0b703e035465d10ed40b46bce2d..46501cbc10a5e242d516eb5c23529d2feb0bac4f 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/SwaggerCorsConfiguration.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/SwaggerCorsConfiguration.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/WebConfiguration.java b/server/src/main/java/de/ozgcloud/antragsraum/WebConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..71a505c8d3f6b944eedd6dce79643f617d569527
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/WebConfiguration.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023-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.antragsraum;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class WebConfiguration {
+	@Value("${clamav.connection.timeoutInS:15}")
+	private int httpConnectionTimeoutInS;
+	@Value("${clamav.read.timeoutInS:60}")
+	private int httpReadTimeoutInS;
+
+	@Bean
+	public RestTemplate restTemplate(RestTemplateBuilder builder) {
+		return builder.setReadTimeout(Duration.of(httpReadTimeoutInS, ChronoUnit.SECONDS))
+		  .setConnectTimeout(Duration.of(httpConnectionTimeoutInS, ChronoUnit.SECONDS)).build();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSender.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac2b961157598151ef985865988374c69189c547
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSender.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import io.grpc.stub.CallStreamObserver;
+import lombok.AllArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
+
+@Log4j2
+@AllArgsConstructor
+class ChunkedFileSender<T> {
+	private final AtomicBoolean hasUploadFile = new AtomicBoolean(true);
+	private final InputStream uploadStream;
+	private final int chunkSize;
+	private final Function<byte[], T> buildChunkRequest;
+	private T requestMetadata;
+
+	public void sendChunkTo(CallStreamObserver<T> streamObserver) {
+		if (hasUploadFile.get()) {
+			sendMetadata(streamObserver);
+			int size = sendNextChunk(streamObserver);
+			if (size < chunkSize) {
+				handleFileEndReached(streamObserver);
+			}
+		}
+	}
+
+	private void sendMetadata(CallStreamObserver<T> streamObserver) {
+		if (requestMetadata != null) {
+			streamObserver.onNext(requestMetadata);
+			requestMetadata = null;
+		}
+	}
+
+	private int sendNextChunk(CallStreamObserver<T> streamObserver) {
+		byte[] content = readFromStream();
+		var size = content.length;
+		if (size > 0) {
+			streamObserver.onNext(buildChunkRequest.apply(content));
+		}
+		return size;
+	}
+
+	private byte[] readFromStream() {
+		try {
+			return uploadStream.readNBytes(chunkSize);
+		} catch (IOException e) {
+			LOG.error("Error on sending a single chunk", e);
+			throw new TechnicalException(e);
+		}
+	}
+
+	private void handleFileEndReached(CallStreamObserver<T> streamObserver) {
+		IOUtils.closeQuietly(uploadStream);
+		streamObserver.onCompleted();
+		hasUploadFile.getAndSet(false);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileController.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileController.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b94af24d973d797bb2d817da24b4c1be4eaaa7b
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileController.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.concurrent.CompletableFuture;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
+
+import de.ozgcloud.antragsraum.common.InvalidFileTypeException;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.ExampleObject;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@RestController
+@RequestMapping(FileController.PATH)
+@RequiredArgsConstructor
+@SecurityRequirement(name = "security_auth")
+public class FileController {
+	public static final String PATH = "/api/file/";
+	static final String PATH_PATTERN = PATH + "%s/%s";
+
+	private final @NonNull FileService fileService;
+	private final @NonNull FileValidator fileValidator;
+
+	@Operation(summary = "Download file content")
+	@GetMapping(value = "{address}/{fileId}", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE,
+	  "images/*" })
+	public ResponseEntity<StreamingResponseBody> getFileData(
+	  @Parameter(description = "The url encoded address of the OZG-Cloud instance", example = "6358fd4146811d04010f44d0")
+	  @PathVariable String address,
+	  @Parameter(description = "The id of the file", example = "6358fd0bee7a051389cdd788") @PathVariable
+	  String fileId) {
+		var decodedAddress = getDecodedAddress(address);
+		var ozgFile = getFile(fileId, decodedAddress);
+
+		return buildResponseEntity(ozgFile, createDownloadStreamingBody(fileId, decodedAddress));
+	}
+
+	OzgFile getFile(String fileId, String address) {
+		return fileService.getFile(fileId, address);
+	}
+
+	private ResponseEntity<StreamingResponseBody> buildResponseEntity(OzgFile ozgFile,
+	  StreamingResponseBody responseBody) {
+		if (ozgFile != null) {
+			return ResponseEntity.ok()
+			  .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", ozgFile.fileName()))
+			  .contentLength(ozgFile.fileSize())
+			  .contentType(MediaType.valueOf(ozgFile.contentType()))
+			  .body(responseBody);
+		} else {
+			return ResponseEntity.notFound().build();
+		}
+	}
+
+	private StreamingResponseBody createDownloadStreamingBody(String fileId, String address) {
+		return out -> fileService.downloadFileContent(fileId, out, address);
+	}
+
+	@Operation(summary = "Upload file content",
+	  description = "You can use cURL to upload a file example: curl -F file=@TestDokument.pdf http://localhost:8080/api/file/[vorgangid]/[address] -v")
+	@ApiResponses(value = {
+	  @ApiResponse(responseCode = "201", description = "The file has been created",
+		content = @Content(examples = @ExampleObject(value = "6358fd0bee7a051389cdd788", description = "The id of the file created")),
+		headers = { @Header(name = "Location", description = "The path of of the uploaded file with its id") })
+	})
+	@PostMapping(value = "{vorgangId}/{address}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+	@Async
+	public CompletableFuture<ResponseEntity<String>> uploadFile(
+	  @Parameter(description = "The id of the Vorgang the clerk message belongs to", example = "6358fd0bee7a051389cdd787")
+	  @PathVariable @NonNull String vorgangId,
+	  @Parameter(description = "The address og the ozg-cloud instance the message come from in urlencoded form", example = "static://localhost:9091")
+	  @PathVariable @NonNull String address,
+	  @RequestBody MultipartFile file) {
+		var decodedAddress = getDecodedAddress(address);
+		LOG.info("Uploading attachment for vorgang {} to address {}", vorgangId, decodedAddress);
+		if (fileValidator.isValid(file)) {
+			var fileIdFuture = fileService.upload(vorgangId, decodedAddress, file);
+
+			return fileIdFuture.thenApply(
+			  fileId -> ResponseEntity.created(URI.create(PATH_PATTERN.formatted(vorgangId, fileId))).body(fileId));
+		} else {
+			LOG.warn("Uploaded File {} is not valid.", file.getName());
+			throw new InvalidFileTypeException(file.getName());
+		}
+	}
+
+	@NonNull
+	private static String getDecodedAddress(String address) {
+		return new String(Base64.getDecoder().decode(address.getBytes(StandardCharsets.UTF_8)));
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileDownloadStreamObserver.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserver.java
similarity index 52%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileDownloadStreamObserver.java
rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserver.java
index e2d0a86b37da842bf72401e1763b88db1947facc..368563ae5f200722690525324ada3383c33ae4c8 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileDownloadStreamObserver.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserver.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,42 +18,46 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
 
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcGetBinaryFileDataResponse;
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataResponse;
 import io.grpc.stub.StreamObserver;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.concurrent.CompletableFuture;
 
+@Log4j2
 @RequiredArgsConstructor(access = AccessLevel.PROTECTED)
 class FileDownloadStreamObserver implements StreamObserver<GrpcGetBinaryFileDataResponse> {
-    private final CompletableFuture<Boolean> streamFuture;
-    private final OutputStream out;
-
-    @Override
-    public void onNext(GrpcGetBinaryFileDataResponse response) {
-        writeToStream(response.getFileContent().toByteArray());
-    }
-
-    void writeToStream(byte[] contentPart) {
-        try {
-            out.write(contentPart);
-        } catch (IOException e) {
-            throw new RuntimeException("Download file error writing on output stream", e);
-        }
-    }
-
-    @Override
-    public void onError(Throwable t) {
-        streamFuture.completeExceptionally(t);
-    }
-
-    @Override
-    public void onCompleted() {
-        streamFuture.complete(true);
-    }
+	private final CompletableFuture<Boolean> streamFuture;
+	private final OutputStream out;
+
+	@Override
+	public void onNext(GrpcGetBinaryFileDataResponse response) {
+		writeToStream(response.getFileContent().toByteArray());
+	}
+
+	void writeToStream(byte[] contentPart) {
+		try {
+			out.write(contentPart);
+		} catch (IOException e) {
+			LOG.error("Download file error writing on output stream", e);
+			throw new TechnicalException(e);
+		}
+	}
+
+	@Override
+	public void onError(Throwable t) {
+		streamFuture.completeExceptionally(t);
+	}
+
+	@Override
+	public void onCompleted() {
+		streamFuture.complete(true);
+	}
 }
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileGrpcClient.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileGrpcClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..93281c94b9a68ffb4eb5d49761f483becbba2e40
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileGrpcClient.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import java.io.OutputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.springframework.stereotype.Component;
+
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.common.ChannelPropertiesFactory;
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFilesRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcFindFilesResponse;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest;
+import io.grpc.stub.StreamObserver;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
+import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+
+@Component
+@RequiredArgsConstructor
+class FileGrpcClient {
+	public static final int CHUNK_SIZE = 255 * 1024;
+
+	private final @NonNull GlobalClientInterceptorRegistry registry;
+	private final @NonNull ChannelPropertiesFactory channelPropertiesFactory;
+	private final @NonNull ContextService contextService;
+
+	GrpcChannelFactory getChannelFactory(String channelAddress) {
+		return de.ozgcloud.antragsraum.common.GrpcChannelFactory.getChannelFactory(channelPropertiesFactory.getGrpcChannelsProperties(channelAddress),
+		  registry);
+	}
+
+	GrpcFindFilesResponse findBinaryFilesMetaData(GrpcBinaryFilesRequest request, String address) {
+		try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
+			var channel = channelFactory.createChannel(address);
+			var serviceStub = BinaryFileServiceGrpc.newBlockingStub(channel);
+
+			return getBinaryFilesMetaData(request, serviceStub);
+		}
+	}
+
+	GrpcFindFilesResponse getBinaryFilesMetaData(GrpcBinaryFilesRequest request,
+	  BinaryFileServiceGrpc.BinaryFileServiceBlockingStub fileServiceBlockingStub) {
+		return fileServiceBlockingStub.findBinaryFilesMetaData(request);
+	}
+
+	StreamObserver<GrpcUploadBinaryFileRequest> uploadFile(OzgUploadFile uploadFile, String address, CompletableFuture<String> fileIdFuture) {
+		try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
+			var channel = channelFactory.createChannel(address);
+			var responseObserver = createUploadFileObserver(fileIdFuture, uploadFile);
+			var serviceStub = BinaryFileServiceGrpc.newStub(channel);
+
+			return doUploadFiles(responseObserver, serviceStub);
+		}
+	}
+
+	StreamObserver<GrpcUploadBinaryFileRequest> doUploadFiles(FileUploadStreamObserver observer,
+	  BinaryFileServiceGrpc.BinaryFileServiceStub asyncServiceStub) {
+		return asyncServiceStub.uploadBinaryFileAsStream(observer);
+	}
+
+	FileUploadStreamObserver createUploadFileObserver(CompletableFuture<String> fileIdFuture, OzgUploadFile uploadFile) {
+		var metadataRequest = buildMetaDataRequest(uploadFile);
+		var streamer = new ChunkedFileSender<>(uploadFile.fileContent(), CHUNK_SIZE, this::buildChunkRequest, metadataRequest);
+		return new FileUploadStreamObserver(fileIdFuture, streamer);
+	}
+
+	private GrpcUploadBinaryFileRequest buildMetaDataRequest(OzgUploadFile uploadFile) {
+		return GrpcUploadBinaryFileRequest.newBuilder()
+		  .setMetadata(GrpcUploadBinaryFileMetaData.newBuilder()
+			.setContext(contextService.createCallContext())
+			.setVorgangId(uploadFile.fileData().getVorgangId())
+			.setContentType(uploadFile.fileData().getContentType())
+			.setFileName(uploadFile.fileData().getFileName()))
+		  .build();
+	}
+
+	GrpcUploadBinaryFileRequest buildChunkRequest(byte[] bytes) {
+		return GrpcUploadBinaryFileRequest.newBuilder().setFileContent((ByteString.copyFrom(bytes))).build();
+	}
+
+	void downloadFileContent(String fileId, OutputStream out, String address) {
+		try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
+			var channel = channelFactory.createChannel(address);
+			var serviceStub = BinaryFileServiceGrpc.newStub(channel);
+
+			doDownloadFileContent(fileId, out, serviceStub);
+		}
+	}
+
+	void doDownloadFileContent(String fileId, OutputStream out, BinaryFileServiceGrpc.BinaryFileServiceStub binaryFileServiceStub) {
+		var streamFuture = new CompletableFuture<Boolean>();
+		var responseObserver = createDownloadFileObserver(streamFuture, out);
+		binaryFileServiceStub.getBinaryFileContent(buildGrpcGetBinaryFileDataRequest(fileId), responseObserver);
+
+		waitUntilFutureToComplete(streamFuture);
+
+	}
+
+	FileDownloadStreamObserver createDownloadFileObserver(CompletableFuture<Boolean> streamFuture, OutputStream out) {
+		return new FileDownloadStreamObserver(streamFuture, out);
+	}
+
+	private GrpcGetBinaryFileDataRequest buildGrpcGetBinaryFileDataRequest(String fileId) {
+		return GrpcGetBinaryFileDataRequest.newBuilder().setFileId(fileId).build();
+	}
+
+	void waitUntilFutureToComplete(CompletableFuture<Boolean> streamFuture) {
+		try {
+			streamFuture.get(10, TimeUnit.MINUTES);
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			throw new TechnicalException(e);
+		} catch (ExecutionException | TimeoutException e) {
+			throw new TechnicalException(e);
+		}
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileProperties.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileProperties.java
similarity index 82%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileProperties.java
rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/FileProperties.java
index fa25ab2587c6cb13bd71b315f401917212cf7bbd..0f508680b5996f8c0ba87e0a54f65f6102a5cd3c 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileProperties.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileProperties.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,7 +18,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -35,7 +32,6 @@ import org.springframework.util.unit.DataSize;
 @ConfigurationProperties(FileProperties.PREFIX)
 class FileProperties {
     static final String PREFIX = "ozgcloud.upload";
-
     private DataSize maxFileSize;
     private String allowedSuffixes;
     private String tempPath = "/tmp/antragsraum";
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRemoteService.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRemoteService.java
new file mode 100644
index 0000000000000000000000000000000000000000..e391acd4e6b5669b1da6f6e4e56aa6613dea0857
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileRemoteService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import java.io.OutputStream;
+import java.util.concurrent.CompletableFuture;
+
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFilesRequest;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Service
+@RequiredArgsConstructor
+public class FileRemoteService {
+	private final @NonNull FileGrpcClient grpcClient;
+
+	public OzgFile getFile(String fileId, String address) {
+		var grpcFileMetadata = grpcClient.findBinaryFilesMetaData(buildGrpcBinaryFileRequest(fileId), address);
+
+		if (!grpcFileMetadata.getFileList().isEmpty()) {
+			return OzgFileMapper.fromGrpcFile(grpcFileMetadata.getFile(0));
+		}
+
+		LOG.warn("Received file id {} from {}, but findBinaryFilesMetaData returned empty file list", fileId, address);
+
+		return null;
+	}
+
+	private GrpcBinaryFilesRequest buildGrpcBinaryFileRequest(String fileId) {
+		return GrpcBinaryFilesRequest.newBuilder()
+		  .addFileId(fileId)
+		  .build();
+	}
+
+	CompletableFuture<String> uploadFile(OzgUploadFile uploadFile, String address) {
+		var fileIdFuture = new CompletableFuture<String>();
+		var request = grpcClient.uploadFile(uploadFile, address, fileIdFuture);
+		LOG.debug("Grpc File Upload Request: {}", request);
+
+		return fileIdFuture;
+	}
+
+	public void downloadFileContent(String fileId, OutputStream out, String address) {
+		grpcClient.downloadFileContent(fileId, out, address);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileService.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileService.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e4b8ec7db0b690602372f3bebf4a10a5824a1f9
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileService.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.CompletableFuture;
+
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.antragsraum.common.VirusFoundException;
+import de.ozgcloud.apilib.file.OzgCloudUploadFile;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Service
+@RequiredArgsConstructor
+class FileService {
+	private final @NonNull FileRemoteService fileRemoteService;
+	private final @NonNull VirusScannerClient scanner;
+
+	OzgFile getFile(String fileId, String address) {
+		return fileRemoteService.getFile(fileId, address);
+	}
+
+	void downloadFileContent(String fileId, OutputStream out, String address) {
+		fileRemoteService.downloadFileContent(fileId, out, address);
+	}
+
+	CompletableFuture<String> upload(String vorgangId, String address, MultipartFile file) {
+		return fileRemoteService.uploadFile(createUploadFile(vorgangId, file), address);
+	}
+
+	OzgUploadFile createUploadFile(String vorgangId, MultipartFile file) throws VirusFoundException {
+		if (isClean(file)) {
+			LOG.info("File {} is clean, preparing upload", file.getOriginalFilename());
+			return OzgUploadFile.builder()
+			  .fileData(OzgCloudUploadFile.builder()
+				.fileName(file.getOriginalFilename())
+				.contentType(file.getContentType())
+				.vorgangId(vorgangId)
+				.build())
+			  .fileContent(getInputStream(file))
+			  .build();
+		}
+
+		LOG.error("Found viruses in file {}", file.getOriginalFilename());
+		throw new VirusFoundException(vorgangId);
+	}
+
+	boolean isClean(MultipartFile file) {
+		return scanner.scan(file).isEmpty();
+	}
+
+	private InputStream getInputStream(MultipartFile file) {
+		try {
+			return file.getInputStream();
+		} catch (IOException e) {
+			LOG.error("Error reading file data as stream.", e);
+			throw new TechnicalException(e);
+		}
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileUploadStreamObserver.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileUploadStreamObserver.java
similarity index 82%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileUploadStreamObserver.java
rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/FileUploadStreamObserver.java
index 650ed656109532359369c6e9790b4c195922afff..534fd691d3c60241e9dcffcd9dba4c8a9f9b7ae3 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileUploadStreamObserver.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileUploadStreamObserver.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,10 +18,10 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
 
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest;
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileResponse;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileResponse;
 import io.grpc.stub.ClientCallStreamObserver;
 import io.grpc.stub.ClientResponseObserver;
 import lombok.AccessLevel;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeValidator.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileValidator.java
similarity index 57%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeValidator.java
rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/FileValidator.java
index ec9ee4b8cc0d7b68d5f78e8dada51553c728cfd9..d20d234c29fb15d52110a371f2bfa5fb12e3231f 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeValidator.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/FileValidator.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,41 +18,55 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
-
-import jakarta.validation.ConstraintValidator;
-import jakarta.validation.ConstraintValidatorContext;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
+package de.ozgcloud.antragsraum.attachments;
 
 import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.unit.DataSize;
+import org.springframework.web.multipart.MultipartFile;
 
 @RequiredArgsConstructor
-class FileTypeValidator implements ConstraintValidator<FileTypeConstraint, UploadOzgFile> {
+@Component
+class FileValidator {
     public static final String DEFAULT_FILE_SUFFIXES_STRING = "pdf,doc,docx,xls,xlsx,txt,png,jpg,jpeg,tif,tiff";
+    static final long DEFAULT_MAX_SIZE = 100;
 
     private final FileProperties fileProperties;
 
-    @Override
-    public boolean isValid(UploadOzgFile value, ConstraintValidatorContext context) {
-        Optional<String> fileNameOptional = Optional.ofNullable(value.fileData().fileName());
+    public boolean isValid(MultipartFile value) {
         var allowedSuffixes = getAllowedFileTypeSuffixes(fileProperties);
 
-        return fileNameOptional.map(fileName -> allowedSuffixes.contains(getFileNameSuffix(fileName).toLowerCase()))
+        var fileNameOptional = Optional.ofNullable(value.getOriginalFilename());
+
+        boolean isValid =
+            fileNameOptional.map(fileName -> allowedSuffixes.contains(getFileNameSuffix(fileName).toLowerCase()))
                 .orElse(Boolean.FALSE);
+
+        return isValid && fileSizeIsValid(value.getSize());
     }
 
     List<String> getAllowedFileTypeSuffixes(FileProperties fileProperties) {
         var suffixedString = Optional.ofNullable(fileProperties.getAllowedSuffixes());
         return suffixedString.map(val -> List.of(val.split(",")))
-                .orElse(List.of(DEFAULT_FILE_SUFFIXES_STRING.split(",")));
+            .orElse(List.of(DEFAULT_FILE_SUFFIXES_STRING.split(",")));
     }
 
     String getFileNameSuffix(@NonNull String fileName) {
         return fileName.toLowerCase(Locale.ROOT).substring(fileName.lastIndexOf(".") + 1);
     }
 
+    public boolean fileSizeIsValid(long fileSize) {
+        var maxFileSize = Optional.ofNullable(fileProperties.getMaxFileSize());
 
+        return maxFileSize.flatMap(
+                dataSize -> {
+                    var val = dataSize.toBytes() > DataSize.ofBytes(fileSize).toBytes();
+                    return Optional.of(val);
+                })
+            .orElse(DataSize.ofMegabytes(DEFAULT_MAX_SIZE).toBytes() > DataSize.ofBytes(fileSize).toBytes());
+    }
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFile.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFile.java
similarity index 90%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFile.java
rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFile.java
index 85683c76bc19be26b0277c86720ef5ab88a3624f..681a8fcb23cac54b33e88d55ad49c21aa3dffcf1 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFile.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFile.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Builder;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFileMapper.java
similarity index 64%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileMapper.java
rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFileMapper.java
index d309876c34170f639b33605ed9f02e287650e2e2..a0f3459a0f0c3121abad5270b3ddf5dbbb35b054 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileMapper.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgFileMapper.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,22 +18,21 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
 
-import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile;
+import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 class OzgFileMapper {
 
-
     static OzgFile fromGrpcFile(GrpcOzgFile grpcFile) {
         return OzgFile.builder()
-                .fileName(grpcFile.getName())
-                .contentType(grpcFile.getContentType())
-                .id(grpcFile.getId())
-                .fileSize(grpcFile.getSize())
-                .build();
+            .id(grpcFile.getId())
+            .fileName(grpcFile.getName())
+            .contentType(grpcFile.getContentType())
+            .fileSize(grpcFile.getSize())
+            .build();
     }
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/UploadOzgFile.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgUploadFile.java
similarity index 69%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/UploadOzgFile.java
rename to server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgUploadFile.java
index 096f4dc2e743d91bf846ca4daa1936495e385ddf..57378090932801029828b151f809419a00f06aaf 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/attachments/UploadOzgFile.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/OzgUploadFile.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,14 +18,14 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
 
+import de.ozgcloud.apilib.file.OzgCloudUploadFile;
+import java.io.InputStream;
 import lombok.Builder;
 import org.springframework.validation.annotation.Validated;
 
-import java.io.InputStream;
-
 @Validated
 @Builder
-public record UploadOzgFile(OzgFile fileData, String vorgangId, @FileMaxSizeConstraint InputStream fileContent) {
+public record OzgUploadFile(OzgCloudUploadFile fileData, InputStream fileContent) {
 }
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/attachments/VirusScannerClient.java b/server/src/main/java/de/ozgcloud/antragsraum/attachments/VirusScannerClient.java
new file mode 100755
index 0000000000000000000000000000000000000000..c4f75c63026ad22db2a42f80f3efbc1541834d8b
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/attachments/VirusScannerClient.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import de.ozgcloud.antragsraum.common.VirusScanException;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Service
+@RequiredArgsConstructor
+public class VirusScannerClient {
+	private static final String FILE_REQUEST_KEY = "FILES";
+
+	@Value("${clamav.scanUrl}")
+	private String clamAVScanUrl;
+
+	private final RestTemplate restTemplate;
+
+	public Set<String> scan(MultipartFile file) {
+
+		Set<String> viruses = new HashSet<>();
+
+		if (Objects.nonNull(file)) {
+			LOG.info("Scanning file {} for viruses using ClamAV at {}", file.getOriginalFilename(), clamAVScanUrl);
+
+			MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
+			requestBody.add(FILE_REQUEST_KEY, file.getResource());
+			HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(requestBody);
+
+			var response = restTemplate.postForEntity(clamAVScanUrl, request, VirusScanResponse.class);
+
+			if (response.getStatusCode().isError()) {
+				LOG.error("Error scanning file for virus. ClamAV Service returned code {}", response.getStatusCode());
+				throw new VirusScanException(file.getName());
+			}
+
+			var responseBody = Objects.requireNonNull(response.getBody());
+			if (!responseBody.isSuccess()) {
+				LOG.error("Scanning file for virus was not successfully. ClamAV Response {}", responseBody);
+				throw new VirusScanException(file.getName());
+			}
+
+			var result = responseBody.getData().getResult()[0];
+			viruses.addAll(Set.of(result.getViruses()));
+		} else {
+			LOG.warn("Tried to scan null file, will return empty ScanResult Set");
+		}
+
+		return viruses;
+	}
+
+	@Getter
+	@Setter
+	static class VirusScanResponse {
+		private VirusScanData data;
+		private boolean success;
+	}
+
+	@Getter
+	@Setter
+	static class VirusScanData {
+		private VirusScanResult[] result;
+	}
+
+	@Getter
+	@Setter
+	static class VirusScanResult {
+		private String name;
+		@JsonProperty("is_infected")
+		private boolean infected;
+		private String[] viruses;
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/callcontext/CallContextAttachingInterceptor.java b/server/src/main/java/de/ozgcloud/antragsraum/callcontext/CallContextAttachingInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..484ed25079adada76719046df55e19d3ddb9d090
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/callcontext/CallContextAttachingInterceptor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.callcontext;
+
+import java.util.Objects;
+
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ClientInterceptor;
+import io.grpc.ForwardingClientCall;
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import net.devh.boot.grpc.client.interceptor.GrpcGlobalClientInterceptor;
+
+@Log4j2
+@GrpcGlobalClientInterceptor
+@RequiredArgsConstructor
+public class CallContextAttachingInterceptor implements ClientInterceptor {
+	private final ContextService contextService;
+
+	@Override
+	public <A, B> ClientCall<A, B> interceptCall(MethodDescriptor<A, B> method, CallOptions callOptions, Channel next) {
+		LOG.info("Method: {}", Objects.nonNull(method) ? method.getFullMethodName() : "unknown");
+		return new CallContextAttachingClientCall<>(next.newCall(method, callOptions));
+	}
+
+	final class CallContextAttachingClientCall<A, B> extends ForwardingClientCall.SimpleForwardingClientCall<A, B> {
+
+		CallContextAttachingClientCall(ClientCall<A, B> delegate) {
+			super(delegate);
+		}
+
+		@Override
+		public void start(Listener<B> responseListener, Metadata headers) {
+			headers.merge(contextService.buildCallContextMetadata());
+			LOG.debug("Set headers to {}", headers);
+			super.start(responseListener, headers);
+		}
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/ContextService.java b/server/src/main/java/de/ozgcloud/antragsraum/callcontext/ContextService.java
similarity index 62%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/ContextService.java
rename to server/src/main/java/de/ozgcloud/antragsraum/callcontext/ContextService.java
index adcf9c295e11e1ae05aba07fc3bacd219fd9bba8..aee0430ec0d8f5d1bed7f86b77c1fcebf214a161 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/ContextService.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/callcontext/ContextService.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,18 +18,19 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.callcontext;
+package de.ozgcloud.antragsraum.callcontext;
 
-import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext;
-import de.mgm.bup.ozg.antragsraum.common.AuthenticationHelper;
+import de.ozgcloud.antragsraum.common.AuthenticationHelper;
+import de.ozgcloud.antragsraum.security.User;
+import de.ozgcloud.vorgang.grpc.command.GrpcCallContext;
 import io.grpc.Metadata;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
-import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
-import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.stereotype.Service;
 
+import java.nio.charset.Charset;
 import java.util.Objects;
 
 @Log4j2
@@ -42,7 +40,8 @@ public class ContextService {
     public static final String CLIENT_NAME = "Antragsraum";
     public static final String KEY_CLIENT_NAME = "CLIENT_NAME-bin";
     public static final String KEY_REQUEST_ID = "REQUEST_ID-bin";
-    public static final String KEY_AUTHENTICATION_ID = "AUTHENTICATION-bin";
+    public static final String KEY_JWT_TOKEN = "JWT_TOKEN-bin";
+    public static final String NO_SAML_TOKEN = "--not set--";
 
     private final @NonNull AuthenticationHelper authenticationHelper;
     RequestAttributes requestAttributes;
@@ -57,9 +56,9 @@ public class ContextService {
     public Metadata buildCallContextMetadata() {
         var metadata = new Metadata();
 
-        metadata.put(createKeyOf(KEY_CLIENT_NAME), getClientName().getBytes());
-        metadata.put(createKeyOf(KEY_REQUEST_ID), getRequestId().getBytes());
-        metadata.put(createKeyOf(KEY_AUTHENTICATION_ID), getAuthenticationToken().getBytes());
+        metadata.put(createKeyOf(KEY_CLIENT_NAME), getClientName().getBytes(Charset.defaultCharset()));
+        metadata.put(createKeyOf(KEY_REQUEST_ID), getRequestId().getBytes(Charset.defaultCharset()));
+        metadata.put(createKeyOf(KEY_JWT_TOKEN), getJwtToken().getBytes(Charset.defaultCharset()));
 
         return metadata;
     }
@@ -72,15 +71,12 @@ public class ContextService {
         return CLIENT_NAME;
     }
 
-    String getAuthenticationToken() {
-        var token = authenticationHelper.getAuthentication();
-        String tokenValue;
-        if (token instanceof JwtAuthenticationToken jwtToken) {
-            tokenValue = jwtToken.getToken().getTokenValue();
-        } else if (token instanceof BearerTokenAuthenticationToken bearerToken) {
-            tokenValue = bearerToken.getToken();
-        } else {
-            tokenValue = "";
+    public String getSamlResponse() {
+        String tokenValue = NO_SAML_TOKEN;
+        var auth = authenticationHelper.getAuthentication();
+
+        if (auth instanceof UsernamePasswordAuthenticationToken userToken && userToken.getPrincipal() instanceof User user) {
+            tokenValue = user.getSamlToken();
         }
 
         return tokenValue;
@@ -89,4 +85,13 @@ public class ContextService {
     String getRequestId() {
         return Objects.isNull(requestAttributes) ? "- no request scope -" : requestAttributes.getRequestId();
     }
+
+    String getJwtToken() {
+        var auth = authenticationHelper.getAuthentication();
+        if (auth instanceof UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) {
+            return (String) usernamePasswordAuthenticationToken.getCredentials();
+        }
+
+        return "";
+    }
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/RequestAttributes.java b/server/src/main/java/de/ozgcloud/antragsraum/callcontext/RequestAttributes.java
similarity index 86%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/RequestAttributes.java
rename to server/src/main/java/de/ozgcloud/antragsraum/callcontext/RequestAttributes.java
index 0762b11f1fa89707cc65dfb97e589a8c7bca0996..497d0a50d615505e9251492e7b9e33126f2623a2 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/callcontext/RequestAttributes.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/callcontext/RequestAttributes.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.callcontext;
+package de.ozgcloud.antragsraum.callcontext;
 
 import lombok.*;
 import org.springframework.context.annotation.Scope;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AddressNotFoundAdvice.java b/server/src/main/java/de/ozgcloud/antragsraum/common/AddressNotFoundAdvice.java
similarity index 89%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AddressNotFoundAdvice.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/AddressNotFoundAdvice.java
index dfd4250dcc63caeaa348756facd2726b9a45cce7..600933f2dcafc71ef2ce984d0f90101e7e8665cb 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AddressNotFoundAdvice.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/AddressNotFoundAdvice.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import jakarta.annotation.Priority;
 import lombok.extern.log4j.Log4j2;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AddressNotFoundException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/AddressNotFoundException.java
similarity index 84%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AddressNotFoundException.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/AddressNotFoundException.java
index 3a2be0ebeab0b457d09260b3d3f0745ab4af4779..0d596dbcc01e16dd7bf99a360450694857642f9b 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AddressNotFoundException.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/AddressNotFoundException.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 public class AddressNotFoundException extends RuntimeException {
     private static final String MESSAGE_TEMPLATE = "No NachrichtEvent for id '%s' found";
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AttachmentNotFoundAdvice.java b/server/src/main/java/de/ozgcloud/antragsraum/common/AttachmentNotFoundAdvice.java
similarity index 89%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AttachmentNotFoundAdvice.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/AttachmentNotFoundAdvice.java
index 0e9de752ed1c134865007021fb100a01f4ff8f1d..a3064ebfd134d95e1b20826789a30e8dbf10caf0 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AttachmentNotFoundAdvice.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/AttachmentNotFoundAdvice.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import jakarta.annotation.Priority;
 import lombok.extern.log4j.Log4j2;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AuthenticationHelper.java b/server/src/main/java/de/ozgcloud/antragsraum/common/AuthenticationHelper.java
similarity index 90%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AuthenticationHelper.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/AuthenticationHelper.java
index 5e86ab2d5d6d71a908a33d82c641658877c02a66..82a4a4aaa686a91296f408c868c650c9226b7409 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/AuthenticationHelper.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/AuthenticationHelper.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,8 +20,10 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
+import java.util.Optional;
+import java.util.function.Predicate;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.security.authentication.AuthenticationTrustResolver;
 import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
@@ -30,9 +31,6 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Component;
 
-import java.util.Optional;
-import java.util.function.Predicate;
-
 @Component
 @Log4j2
 public class AuthenticationHelper {
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/ChannelPropertiesFactory.java b/server/src/main/java/de/ozgcloud/antragsraum/common/ChannelPropertiesFactory.java
similarity index 93%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/ChannelPropertiesFactory.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/ChannelPropertiesFactory.java
index 68c69bc79d47f8a29fa8aa533381384084ccee04..e6de8f92e0c4a15488f965382853e084cb83712f 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/ChannelPropertiesFactory.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/ChannelPropertiesFactory.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/GeneralExceptionAdvice.java b/server/src/main/java/de/ozgcloud/antragsraum/common/GeneralExceptionAdvice.java
similarity index 87%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/GeneralExceptionAdvice.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/GeneralExceptionAdvice.java
index 4e56a9b189adf79d6436f3c8bb37871a09fd29ab..57e7374408a86c008afb86479291fadc96b5fe88 100755
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/GeneralExceptionAdvice.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/GeneralExceptionAdvice.java
@@ -1,46 +1,45 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.common;
-
-import jakarta.annotation.Priority;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
-
-@ControllerAdvice
-@Log4j2
-@Priority(value = 9)
-public class GeneralExceptionAdvice extends ResponseEntityExceptionHandler {
-    @ExceptionHandler(value = {RuntimeException.class, TechnicalException.class})
-    @ResponseBody
-    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
-    String handleException(RuntimeException ex) {
-        LOG.error("General error.", ex);
-        return "General error processing your request.";
-    }
-}
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+import jakarta.annotation.Priority;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+@Log4j2
+@Priority(value = 9)
+public class GeneralExceptionAdvice extends ResponseEntityExceptionHandler {
+    @ExceptionHandler(value = {RuntimeException.class, TechnicalException.class})
+    @ResponseBody
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    String handleException(RuntimeException ex) {
+        LOG.error("General error.", ex);
+        return "General error processing your request.";
+    }
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/GrpcChannelFactory.java b/server/src/main/java/de/ozgcloud/antragsraum/common/GrpcChannelFactory.java
similarity index 59%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/GrpcChannelFactory.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/GrpcChannelFactory.java
index 61854248e269d9423459b4546c889b882e296850..a569755b6bd2131b56cb505e1e2be15e7db97d9e 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/GrpcChannelFactory.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/GrpcChannelFactory.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
@@ -37,13 +36,13 @@ import java.util.List;
 
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class GrpcChannelFactory {
-    private static final List<GrpcChannelConfigurer> defaultChannelConfigurers = Collections.emptyList();
+	private static final List<GrpcChannelConfigurer> defaultChannelConfigurers = Collections.emptyList();
 
-    public static net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory getChannelFactory(GrpcChannelsProperties channelsProperties, GlobalClientInterceptorRegistry globalClientInterceptorRegistry) {
-        final ShadedNettyChannelFactory channelFactory = new ShadedNettyChannelFactory(channelsProperties, globalClientInterceptorRegistry, defaultChannelConfigurers);
+	public static net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory getChannelFactory(GrpcChannelsProperties channelsProperties, GlobalClientInterceptorRegistry globalClientInterceptorRegistry) {
+		final ShadedNettyChannelFactory channelFactory = new ShadedNettyChannelFactory(channelsProperties, globalClientInterceptorRegistry, defaultChannelConfigurers);
 
-        final InProcessChannelFactory inProcessChannelFactory =
-                new InProcessChannelFactory(channelsProperties, globalClientInterceptorRegistry, defaultChannelConfigurers);
-        return new InProcessOrAlternativeChannelFactory(channelsProperties, inProcessChannelFactory, channelFactory);
-    }
+		final InProcessChannelFactory inProcessChannelFactory =
+				new InProcessChannelFactory(channelsProperties, globalClientInterceptorRegistry, defaultChannelConfigurers);
+		return new InProcessOrAlternativeChannelFactory(channelsProperties, inProcessChannelFactory, channelFactory);
+	}
 }
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/common/InsufficientTrustLevelException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/InsufficientTrustLevelException.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd27156afdcdc3bfc8eabfc5c52bb29f8da7f36a
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/InsufficientTrustLevelException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+public class InsufficientTrustLevelException extends RuntimeException {
+	private static final String MESSAGE_TEMPLATE = "Current user does not have the required trust level for the Rueckfrage with id '%s'.";
+
+	public InsufficientTrustLevelException(String rueckfrageId) {
+		super(String.format(MESSAGE_TEMPLATE, rueckfrageId));
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/common/InvalidFileAdvice.java b/server/src/main/java/de/ozgcloud/antragsraum/common/InvalidFileAdvice.java
new file mode 100755
index 0000000000000000000000000000000000000000..375e174891763498e30b3b9ef01bae4e8e9c776d
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/InvalidFileAdvice.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+import jakarta.annotation.Priority;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+@Log4j2
+@Priority(value = 0)
+public class InvalidFileAdvice extends ResponseEntityExceptionHandler {
+    @ExceptionHandler(value = {InvalidFileTypeException.class})
+    protected ResponseEntity<Object> handleVirusFound(RuntimeException ex, WebRequest request) {
+        LOG.error("Uploaded file is not valid.", ex);
+        return handleExceptionInternal(ex, "File not acceptable", new HttpHeaders(), HttpStatus.NOT_ACCEPTABLE, request);
+    }
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/common/InvalidFileTypeException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/InvalidFileTypeException.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef2d2293003de12924bc282b13dd1edf25e7f924
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/InvalidFileTypeException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+public class InvalidFileTypeException extends RuntimeException {
+    private static final String MESSAGE_TEMPLATE = "File '%s' has an unsupported file type";
+
+    public InvalidFileTypeException(String fileName) {
+        super(String.format(MESSAGE_TEMPLATE, fileName));
+    }
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/NotFoundException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/NotFoundException.java
similarity index 85%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/NotFoundException.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/NotFoundException.java
index 1b54130b6257a9bded037ff77d2ff55535d49b76..543be5a1a1b074101087f240a9d8c6f05402616b 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/NotFoundException.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/NotFoundException.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 public class NotFoundException extends RuntimeException {
     private static final String MESSAGE_TEMPLATE = "Object '%s' with id '%s' not found.";
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutAdvice.java b/server/src/main/java/de/ozgcloud/antragsraum/common/SendTimeoutAdvice.java
similarity index 87%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutAdvice.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/SendTimeoutAdvice.java
index 370f87e386bb97db2a926939b88dafd06d87403c..39ee5a627bc762039f6ff9414f21fd46258e595f 100755
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutAdvice.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/SendTimeoutAdvice.java
@@ -1,46 +1,45 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.common;
-
-import jakarta.annotation.Priority;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
-
-@ControllerAdvice
-@Log4j2
-@Priority(value = 0)
-public class SendTimeoutAdvice extends ResponseEntityExceptionHandler {
-    @ResponseBody
-    @ExceptionHandler(SendTimeoutException.class)
-    @ResponseStatus(HttpStatus.GATEWAY_TIMEOUT)
-    String sendTimeoutHandler(SendTimeoutException ex) {
-        LOG.error("Timeout while sending answer. Message: " + ex.getMessage(), ex);
-        return "Timeout while sending answer";
-    }
-}
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+import jakarta.annotation.Priority;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+@Log4j2
+@Priority(value = 0)
+public class SendTimeoutAdvice extends ResponseEntityExceptionHandler {
+    @ResponseBody
+    @ExceptionHandler(SendTimeoutException.class)
+    @ResponseStatus(HttpStatus.GATEWAY_TIMEOUT)
+    String sendTimeoutHandler(SendTimeoutException ex) {
+        LOG.error("Timeout while sending answer. Message: " + ex.getMessage(), ex);
+        return "Timeout while sending answer";
+    }
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/SendTimeoutException.java
similarity index 83%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutException.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/SendTimeoutException.java
index 9733c0a43a12892ea034dd6e2be476d12b98a2c7..88db4a8b78b738b2ecddffd6c43a4d703e40ddf1 100755
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutException.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/SendTimeoutException.java
@@ -1,32 +1,31 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.common;
-
-public class SendTimeoutException extends RuntimeException {
-    private static final String MESSAGE_TEMPLATE = "A timeout sending a nachricht happened. Command %s did not finish in time! Message: %s";
-
-    public SendTimeoutException(String commandId, Throwable ex) {
-        super(String.format(MESSAGE_TEMPLATE, commandId, ex.getMessage()), ex);
-    }
-}
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+public class SendTimeoutException extends RuntimeException {
+    private static final String MESSAGE_TEMPLATE = "A timeout sending a nachricht happened. Command %s did not finish in time! Message: %s";
+
+    public SendTimeoutException(String commandId, Throwable ex) {
+        super(String.format(MESSAGE_TEMPLATE, commandId, ex.getMessage()), ex);
+    }
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/TechnicalException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/TechnicalException.java
similarity index 84%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/TechnicalException.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/TechnicalException.java
index 929aacdbcdea169c43c17d095dffff447bacba43..f3fd6b1923808c818b893c2879c3e002f129d950 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/TechnicalException.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/TechnicalException.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 public class TechnicalException extends RuntimeException {
     private static final String MESSAGE_TEMPLATE = "TechnicalException happened! Message: ";
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/VirusFoundAdvice.java b/server/src/main/java/de/ozgcloud/antragsraum/common/VirusFoundAdvice.java
similarity index 89%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/VirusFoundAdvice.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/VirusFoundAdvice.java
index 5576997b08a2930b6029407912440df24443b5a4..6dc0b12e0e0ed8e2b511b9d43a9158b395a16568 100755
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/VirusFoundAdvice.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/VirusFoundAdvice.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import jakarta.annotation.Priority;
 import lombok.extern.log4j.Log4j2;
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/VirusFoundException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/VirusFoundException.java
similarity index 84%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/VirusFoundException.java
rename to server/src/main/java/de/ozgcloud/antragsraum/common/VirusFoundException.java
index 4a72911c74b3bcdce71adc48d955c2fad4f29501..6ca6e78da97efc6fe13a267e446a3553efea24c2 100755
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/common/VirusFoundException.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/VirusFoundException.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,7 +20,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 public class VirusFoundException extends RuntimeException {
     private static final String MESSAGE_TEMPLATE = "File with nachrichtId '%s' contains virus.";
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/common/VirusScanException.java b/server/src/main/java/de/ozgcloud/antragsraum/common/VirusScanException.java
new file mode 100644
index 0000000000000000000000000000000000000000..f130b513e469ebc25ef9be032e1a0f2165de0267
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/common/VirusScanException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+public class VirusScanException extends RuntimeException {
+	private static final String MESSAGE_TEMPLATE = "Virus scan for file with name '%s' failed.";
+
+	public VirusScanException(String fileName) {
+		super(String.format(MESSAGE_TEMPLATE, fileName));
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEvent.java b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEvent.java
similarity index 74%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEvent.java
rename to server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEvent.java
index b8a149f99714c7feee9a681c4e8f6545659bcc66..1d03a40f0a4520cc3ab4f90138780177e782f046 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEvent.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEvent.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,10 +20,10 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.events;
+package de.ozgcloud.antragsraum.events;
 
 import lombok.Builder;
 
 @Builder
-public record NachrichtEvent(String vorgangId, String postfachId, String rueckfrageId) {
+public record NachrichtEvent(String postfachId, String address) {
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventMapper.java
similarity index 63%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventMapper.java
rename to server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventMapper.java
index 6881cb6450ec3a504d566dd01fd146527d16645b..b70e068e5e086f62b8be75f701ee822c8a7daa09 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventMapper.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventMapper.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,18 +20,17 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.events;
+package de.ozgcloud.antragsraum.events;
 
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcNachricht;
+import de.ozgcloud.info.nachricht.GrpcNachricht;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 class NachrichtEventMapper {
-    static NachrichtEvent fromGrpc(GrpcNachricht nachricht) {
-        return NachrichtEvent.builder()
-                .rueckfrageId(nachricht.getNachrichtId())
-                .vorgangId(nachricht.getVorgangId())
-                .postfachId(nachricht.getPostfachId()).build();
-    }
+	static NachrichtEvent fromGrpc(GrpcNachricht nachricht) {
+		return NachrichtEvent.builder()
+		  .postfachId(nachricht.getPostfachId())
+		  .address(nachricht.getNachrichtenListUrl()).build();
+	}
 }
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteService.java b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteService.java
new file mode 100644
index 0000000000000000000000000000000000000000..215b5880de1a60eba164f1ba3de8acb4fa0da243
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteService.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.events;
+
+import de.ozgcloud.info.GrpcInformationRequest;
+import de.ozgcloud.info.InformationServiceGrpc;
+import de.ozgcloud.info.nachricht.GrpcNachricht;
+import lombok.extern.log4j.Log4j2;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@Log4j2
+class NachrichtEventRemoteService {
+
+	@GrpcClient("info-manager")
+	InformationServiceGrpc.InformationServiceBlockingStub informationServiceBlockingClient;
+
+	List<NachrichtEvent> getNachrichtEventsOfPostfach(String postfachId) {
+		return loadEvents(postfachId).stream().map(NachrichtEventMapper::fromGrpc).toList();
+	}
+
+	private List<GrpcNachricht> loadEvents(String postfachId) {
+		var request = GrpcInformationRequest.newBuilder().setPostfachId(postfachId).build();
+
+		return informationServiceBlockingClient.getInformation(request).getNachrichtenList();
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventService.java b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventService.java
similarity index 57%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventService.java
rename to server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventService.java
index 4ebd1eeaa84446e84ceb316e7e0e8787bcb253ee..f8143ba0c875ff9afd9492f985f000c3f310a10e 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventService.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/events/NachrichtEventService.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,9 +20,8 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.events;
+package de.ozgcloud.antragsraum.events;
 
-import de.mgm.bup.ozg.antragsraum.common.AddressNotFoundException;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
@@ -33,16 +31,9 @@ import java.util.List;
 @Service
 @RequiredArgsConstructor
 public class NachrichtEventService {
-    private final @NonNull NachrichtEventRemoteService remoteService;
+	private final @NonNull NachrichtEventRemoteService remoteService;
 
-    public List<NachrichtEvent> getNachrichtEventsOfPostfachId(String postfachId) {
-        return remoteService.getNachrichtEventsOfPostfach(postfachId);
-    }
-
-    public String getServiceUrlOfNachricht(String nachrichtId) {
-        var addressOptional = remoteService.getServiceUrl(nachrichtId);
-        return addressOptional.orElseThrow(
-                () -> new AddressNotFoundException(nachrichtId)
-        );
-    }
+	public List<NachrichtEvent> getNachrichtEventsOfPostfachId(String postfachId) {
+		return remoteService.getNachrichtEventsOfPostfach(postfachId);
+	}
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/PostfachGrpcServiceStub.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/AntragsraumGrpcServiceStub.java
similarity index 60%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/PostfachGrpcServiceStub.java
rename to server/src/main/java/de/ozgcloud/antragsraum/mocks/AntragsraumGrpcServiceStub.java
index 9c417e698029fd9d18bda5b16f9e330e5f7f13c9..bbf172496c49fc99371696e6c999f90b5e96d3a1 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/PostfachGrpcServiceStub.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/AntragsraumGrpcServiceStub.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,11 +18,14 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.mocks;
+package de.ozgcloud.antragsraum.mocks;
 
-import de.itvsh.ozg.mail.postfach.*;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCommand;
-import de.mgm.bup.ozg.antragsraum.common.NotFoundException;
+import de.ozgcloud.antragsraum.common.NotFoundException;
+import de.ozgcloud.nachrichten.antragraum.AntragraumServiceGrpc;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerResponse;
 import io.grpc.stub.StreamObserver;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
@@ -36,11 +36,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 @GrpcService
 @ConditionalOnProperty(prefix = "grpc", name = "ozg_service.stubs.enabled")
 @RequiredArgsConstructor
-public class PostfachGrpcServiceStub extends PostfachServiceGrpc.PostfachServiceImplBase {
+public class AntragsraumGrpcServiceStub extends AntragraumServiceGrpc.AntragraumServiceImplBase {
     private final StubDataRepository dataRepository;
 
     @Override
-    public void findRueckfragen(GrpcFindRueckfragenRequest request, StreamObserver<GrpcFindRueckfragenResponse> responseObserver) {
+    public void findRueckfragen(GrpcFindRueckfragenRequest request,
+                                StreamObserver<GrpcFindRueckfragenResponse> responseObserver) {
         try {
             var response = dataRepository.getRueckfragenResponse(request.getPostfachId());
             LOG.info("Send response " + response);
@@ -54,23 +55,10 @@ public class PostfachGrpcServiceStub extends PostfachServiceGrpc.PostfachService
     }
 
     @Override
-    public void getRueckfrage(GrpcGetRueckfrageRequest request, StreamObserver<GrpcGetRueckfrageResponse> responseObserver) {
-        try {
-            var response = dataRepository.getRueckfrageResponse(request.getRueckfrageId());
-            LOG.info("Send response " + response);
-            responseObserver.onNext(response);
-
-            responseObserver.onCompleted();
-        } catch (NotFoundException e) {
-            LOG.error(e);
-            responseObserver.onError(e);
-        }
-    }
-
-    @Override
-    public void sendRueckfrageAnswer(GrpcRueckfrageAnswerRequest request, StreamObserver<GrpcCommand> responseObserver) {
+    public void sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequest request,
+                                     StreamObserver<GrpcSendRueckfrageAnswerResponse> responseObserver) {
         LOG.info("Sending answer to OZG-CLOUD: " + request);
-        var response = GrpcCommand.newBuilder().setStatus(CommandGrpcServiceStub.STATUS_SEND).setId(CommandGrpcServiceStub.ID).build();
+        var response = GrpcSendRueckfrageAnswerResponse.newBuilder().setCommandId(CommandGrpcServiceStub.ID).build();
         var res = dataRepository.addReply(request.getAnswer());
 
         if (res) {
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/BinaryFileGrpcServiceStub.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/BinaryFileGrpcServiceStub.java
new file mode 100644
index 0000000000000000000000000000000000000000..f5269c0cac498e83d66ff064c0df0676faf3b76f
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/BinaryFileGrpcServiceStub.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.mocks;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFilesRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcFindFilesResponse;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataResponse;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileResponse;
+import io.grpc.stub.StreamObserver;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import net.devh.boot.grpc.server.service.GrpcService;
+
+@Log4j2
+@GrpcService
+@ConditionalOnProperty(prefix = "grpc", name = "ozg_service.stubs.enabled")
+@RequiredArgsConstructor
+public class BinaryFileGrpcServiceStub extends BinaryFileServiceGrpc.BinaryFileServiceImplBase {
+	public static final int CHUNK_SIZE = 255 * 1024;
+	private final StubDataRepository stubDataRepository;
+
+	@Override
+	public StreamObserver<GrpcUploadBinaryFileRequest> uploadBinaryFileAsStream(StreamObserver<GrpcUploadBinaryFileResponse> responseObserver) {
+		return new UploadStreamObserverStub(responseObserver, stubDataRepository);
+		/*var fileId = stubDataRepository.addFile(fileUploadRequest.getMetadata());
+		var fileIdFuture = CompletableFuture.completedFuture(fileId);
+		var streamer = new ChunkedFileSender(uploadBinaryFileRequest.getUploadStream(), CHUNK_SIZE, this::buildChunkRequest, metadataRequest);
+		return new BinaryFileUploadStreamObserver(fileIdFuture, streamer);*/
+	}
+
+	@Override
+	public void getBinaryFileContent(GrpcGetBinaryFileDataRequest request, StreamObserver<GrpcGetBinaryFileDataResponse> responseObserver) {
+		var contentOpt = stubDataRepository.getFileContent(request.getFileId());
+
+		contentOpt.ifPresentOrElse(content -> {
+			try (ByteArrayInputStream buffer = new ByteArrayInputStream(content)) {
+				byte[] chunk = new byte[CHUNK_SIZE];
+				int read;
+				while ((read = buffer.read(chunk)) > 0) {
+					LOG.info("Send chunk. Length: {}", read);
+					responseObserver.onNext(GrpcGetBinaryFileDataResponse.newBuilder()
+					  .setFileContent(ByteString.copyFrom(chunk)).build());
+				}
+			} catch (IOException e) {
+				responseObserver.onError(e);
+			}
+		}, () -> {
+			try (ByteArrayInputStream bais = new ByteArrayInputStream(GrpcDataCreator.FILE_CONTENT)) {
+				responseObserver.onNext(GrpcGetBinaryFileDataResponse.newBuilder()
+				  .setFileContent(ByteString.copyFrom(bais.readAllBytes())).build());
+			} catch (IOException e) {
+				responseObserver.onError(e);
+			}
+		});
+
+		responseObserver.onCompleted();
+	}
+
+	@Override
+	public void findBinaryFilesMetaData(GrpcBinaryFilesRequest request, StreamObserver<GrpcFindFilesResponse> responseObserver) {
+		final String fileId = request.getFileId(0);
+		var fileOpt = stubDataRepository.getFileMetaData(fileId);
+
+		fileOpt.ifPresentOrElse(file -> {
+			responseObserver.onNext(
+			  GrpcFindFilesResponse.newBuilder().addFile(DataMapper.fromGrpcUploadBinaryFileMetaData(file, fileId)).build());
+			responseObserver.onCompleted();
+		}, () -> {
+			LOG.error("File with id {} not found.", fileId);
+			responseObserver.onError(new FileNotFoundException("File " + fileId + " not found in db"));
+		});
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/BinaryFileUploadStreamObserver.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/BinaryFileUploadStreamObserver.java
new file mode 100644
index 0000000000000000000000000000000000000000..661b0c4c5a76d350bb79ce9a6dfb186248e568fe
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/BinaryFileUploadStreamObserver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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.antragsraum.mocks;
+
+import java.util.concurrent.CompletableFuture;
+
+import de.ozgcloud.common.binaryfile.FileId;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileResponse;
+import io.grpc.stub.ClientCallStreamObserver;
+import io.grpc.stub.ClientResponseObserver;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
+class BinaryFileUploadStreamObserver implements ClientResponseObserver<GrpcUploadBinaryFileRequest, GrpcUploadBinaryFileResponse> {
+
+	private final CompletableFuture<FileId> fileIdFuture;
+	private final MockDownloadChunkedFileSender<GrpcUploadBinaryFileRequest> fileStreamer;
+
+	@Getter
+	private String fileId;
+
+	private ClientCallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver;
+
+	@Override
+	public void beforeStart(ClientCallStreamObserver<GrpcUploadBinaryFileRequest> requestStream) {
+		this.requestObserver = requestStream;
+		requestObserver.setOnReadyHandler(() -> fileStreamer.sendChunkTo(requestObserver));
+	}
+
+	@Override
+	public void onNext(GrpcUploadBinaryFileResponse response) {
+		fileId = response.getFileId();
+	}
+
+	@Override
+	public void onError(Throwable t) {
+		fileIdFuture.completeExceptionally(t);
+	}
+
+	@Override
+	public void onCompleted() {
+		fileIdFuture.complete(FileId.from(fileId));
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/CallContextServerInterceptor.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/CallContextServerInterceptor.java
similarity index 53%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/CallContextServerInterceptor.java
rename to server/src/main/java/de/ozgcloud/antragsraum/mocks/CallContextServerInterceptor.java
index 2609e4051a8a4df9d6bee5a16d1ed5d5c5b164f0..616e08cce692097be20331b65cd7c003587f7f4a 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/CallContextServerInterceptor.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/CallContextServerInterceptor.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,9 +18,9 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.mocks;
+package de.ozgcloud.antragsraum.mocks;
 
-import de.mgm.bup.ozg.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.callcontext.ContextService;
 import io.grpc.Metadata;
 import io.grpc.ServerCall;
 import io.grpc.ServerCallHandler;
@@ -33,22 +30,23 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
 import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
+
+import java.util.Objects;
 
 @Log4j2
 @GrpcGlobalServerInterceptor
 @RequiredArgsConstructor
 public class CallContextServerInterceptor implements ServerInterceptor {
-    private final @NonNull ContextService contextService;
+	private final @NonNull JwtTokenWrapper tokenWrapper;
 
-    @Override
-    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
-        var token = new String(headers.get(ContextService.createKeyOf(ContextService.KEY_AUTHENTICATION_ID)));
-        SecurityContextHolder.getContext().setAuthentication(new BearerTokenAuthenticationToken(token));
+	@Override
+	public <R, T> ServerCall.Listener<R> interceptCall(ServerCall<R, T> call, Metadata headers, ServerCallHandler<R, T> next) {
+		var requestId = new String(Objects.requireNonNull(headers.get(ContextService.createKeyOf(ContextService.KEY_REQUEST_ID))));
+		var jwtToken = new String(Objects.requireNonNull(headers.get(ContextService.createKeyOf(ContextService.KEY_JWT_TOKEN))));
 
-        var requestId = new String(headers.get(ContextService.createKeyOf(ContextService.KEY_REQUEST_ID)));
+		SecurityContextHolder.getContext().setAuthentication(tokenWrapper.wrap(jwtToken));
 
-        LOG.info("Request Id {}; Set Auth token {}", requestId, token);
-        return next.startCall(call, headers);
-    }
+		LOG.info("Request Id {}; Set JWT token {}", requestId, jwtToken);
+		return next.startCall(call, headers);
+	}
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/CommandGrpcServiceStub.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/CommandGrpcServiceStub.java
similarity index 78%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/CommandGrpcServiceStub.java
rename to server/src/main/java/de/ozgcloud/antragsraum/mocks/CommandGrpcServiceStub.java
index b66057622a1a98068d2d3ea21ba09e48ee3b6bae..fcc71f5aa280b0496fe9fc86d514636bd70319db 100755
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/CommandGrpcServiceStub.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/CommandGrpcServiceStub.java
@@ -1,55 +1,54 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.mocks;
-
-import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCommand;
-import de.itvsh.ozg.pluto.grpc.command.GrpcGetCommandRequest;
-import io.grpc.stub.StreamObserver;
-import lombok.extern.log4j.Log4j2;
-import net.devh.boot.grpc.server.service.GrpcService;
-
-import java.util.UUID;
-
-@GrpcService
-@Log4j2
-public class CommandGrpcServiceStub extends CommandServiceGrpc.CommandServiceImplBase {
-    static final String ID = UUID.randomUUID().toString();
-    static final String STATUS_SEND = "SEND";
-    static final String STATUS_FINISHED = "FINISHED";
-    static final String STATUS_PENDING = "PENDING";
-
-    @Override
-    public void getCommand(GrpcGetCommandRequest request, StreamObserver<GrpcCommand> observer) {
-        if (ID.equals(request.getId())) {
-            LOG.info("Finishing command {}", request.getId());
-            observer.onNext(GrpcCommand.newBuilder().setStatus(STATUS_FINISHED).setId(ID).build());
-        } else {
-            LOG.info("Pending command {}", request.getId());
-            observer.onNext(GrpcCommand.newBuilder()
-                    .setStatus(STATUS_PENDING).setId(ID).build());
-        }
-        observer.onCompleted();
-    }
-}
+/*
+ * Copyright (c) 2023-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.antragsraum.mocks;
+
+import de.ozgcloud.vorgang.grpc.command.CommandServiceGrpc;
+import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
+import de.ozgcloud.vorgang.grpc.command.GrpcGetCommandRequest;
+import io.grpc.stub.StreamObserver;
+import lombok.extern.log4j.Log4j2;
+import net.devh.boot.grpc.server.service.GrpcService;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+import java.util.UUID;
+
+@GrpcService
+@Log4j2
+@ConditionalOnProperty(prefix = "grpc", name = "ozg_service.stubs.enabled")
+public class CommandGrpcServiceStub extends CommandServiceGrpc.CommandServiceImplBase {
+    static final String ID = UUID.randomUUID().toString();
+    static final String STATUS_SEND = "SEND";
+    static final String STATUS_FINISHED = "FINISHED";
+    static final String STATUS_PENDING = "PENDING";
+
+    @Override
+    public void getCommand(GrpcGetCommandRequest request, StreamObserver<GrpcCommand> observer) {
+        if (ID.equals(request.getId())) {
+            LOG.info("Finishing command {}", request.getId());
+            observer.onNext(GrpcCommand.newBuilder().setStatus(STATUS_FINISHED).setId(ID).build());
+        } else {
+            LOG.info("Pending command {}", request.getId());
+            observer.onNext(GrpcCommand.newBuilder()
+                    .setStatus(STATUS_PENDING).setId(ID).build());
+        }
+        observer.onCompleted();
+    }
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/DataMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/DataMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f253a13d5e0a45084d961a0448bd98a0aec63f1
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/DataMapper.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.mocks;
+
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
+import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Log4j2
+public class DataMapper {
+
+	static GrpcOzgFile fromGrpcUploadBinaryFileMetaData(GrpcUploadBinaryFileMetaData fileMetaData, String fileId) {
+		return GrpcOzgFile.newBuilder()
+				.setContentType(fileMetaData.getContentType())
+				.setId(fileId)
+				.setName(fileMetaData.getFileName())
+				.setSize(fileMetaData.getSize()).build();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/E2eTestController.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/E2eTestController.java
new file mode 100644
index 0000000000000000000000000000000000000000..273633df7f8f6d160d08766dfff23e8aee1b5852
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/E2eTestController.java
@@ -0,0 +1,93 @@
+/*
+ * 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.antragsraum.mocks;
+
+import de.ozgcloud.antragsraum.events.NachrichtEvent;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFile;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.UUID;
+
+import static de.ozgcloud.antragsraum.mocks.GrpcDataCreator.*;
+
+@RestController
+@Log4j2
+@RequestMapping(E2eTestController.PATH)
+@RequiredArgsConstructor
+@ConditionalOnProperty(name = "ozgcloud.mock.auth")
+public class E2eTestController {
+	static final String CONTENT_TYPE = "text/plain";
+	private static final String DATA = "Inhalt der Testdatei ";
+	public static final String PATH = "/api/e2e/";
+
+	private final StubDataRepository stubDataRepository;
+	private final WritingNachrichtenEventService nachrichtEventService;
+
+	@Operation(summary = "Create a Nachricht and send it to the InfoManager")
+	@PostMapping(value = "event", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+	public ResponseEntity<NachrichtEvent> sendNachricht(@RequestBody @Parameter(description = "The Testevent data as JSON") MockEvent mockEvent) {
+
+		var event = nachrichtEventService.addEvent(mockEvent);
+		var nachrichtId = createNachricht(event, mockEvent);
+
+		LOG.info("Created Nachricht Id: {}", nachrichtId);
+
+		return ResponseEntity.ok().body(event);
+	}
+
+	String createNachricht(NachrichtEvent nachrichtEvent, MockEvent mockEvent) {
+		var reply = GrpcDataCreator.getRueckfrageBuilder(mockEvent.vorgangId())
+				.setText(StringUtils.isNotEmpty(mockEvent.clerkMessageText()) ? mockEvent.clerkMessageText() : TEXT)
+				.setVorgangName(StringUtils.isNotEmpty(mockEvent.clerkMessageTitle()) ? mockEvent.clerkMessageTitle() : VORGANG_TITLE + mockEvent.vorgangId())
+				.setSentAt(StringUtils.isNotEmpty(mockEvent.sendDateTime()) ? mockEvent.sendDateTime() : LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
+		if (mockEvent.attachmentCount() > 0) {
+			for (int i = 0; i < mockEvent.attachmentCount(); i++) {
+				var fileId = UUID.randomUUID().toString();
+				var fileContent = (DATA + i).getBytes();
+				var file = GrpcBinaryFile.newBuilder()
+						.setId(fileId)
+						.setName("TestDoc" + i + ".txt")
+						.setContentType(CONTENT_TYPE)
+						.setSize(fileContent.length).build();
+				reply.addAttachmentFileId(file.getId());
+
+				stubDataRepository.setFile(file, reply.getVorgangId());
+				stubDataRepository.addFileContent(fileId, fileContent);
+			}
+		}
+
+		return String.valueOf(stubDataRepository.addReply(reply.build(), nachrichtEvent));
+	}
+}
+
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcDataCreator.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcDataCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..38077504dce17523f3a607c7176f3985d011d940
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcDataCreator.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.mocks;
+
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrage;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFile;
+import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.http.MediaType;
+
+import java.nio.charset.StandardCharsets;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Log4j2
+class GrpcDataCreator {
+	static final String POSTFACH_ID_1 = "28721c6f-b78f-4d5c-a048-19fd2fc429d2";
+	static final String POSTFACH_ID_2 = "12345c6a-b78f-4d5c-a048-19fd2fc400a1";
+	static final String NACHRICHT_ID_1_0 = "6358fd4146811d04010f44d0";
+	static final String NACHRICHT_ID_2_0 = "6358fd4446811d04010f44da";
+	static final String NACHRICHT_ID_2_1 = "6358fd4446811d04010f44db";
+	static final String VORGANG_ID_1 = "6358fd0bee7a051389cdd787";
+	static final String VORGANG_ID_2 = "6358fd3f46811d04010f44c7";
+	public static final String TEXT = """
+			Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
+			 sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
+			 sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
+			 
+			 Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+			 Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
+			""";
+	static final String FILE_ID_1 = "6358fd0bee7a051389cdd788";
+	static final String FILE_ID_2 = "6358fd0bee7a051389cdd799";
+	static final String FILE_NAME = "TestDokument.txt";
+	static final byte[] FILE_CONTENT = TEXT.getBytes(StandardCharsets.UTF_8);
+	static final String VORGANG_TITLE = "Vorgang title ";
+
+	static GrpcRueckfrage.Builder getRueckfrageBuilder(String vorgangId) {
+		var builder = GrpcRueckfrage.newBuilder()
+				.setId(RandomStringUtils.randomAlphanumeric(16))
+				.setVorgangId(vorgangId)
+				.setText(TEXT)
+				.setVorgangName(VORGANG_TITLE + vorgangId)
+				.setSentAt("2022-10-26T09:26:25")
+				.clearAnswers();
+		if (vorgangId.equals(VORGANG_ID_1)) {
+			builder.addAttachmentFileId(FILE_ID_1);
+			builder.setId(NACHRICHT_ID_1_0);
+		} else if (vorgangId.equals(VORGANG_ID_2)) {
+			builder.setId(NACHRICHT_ID_2_0);
+			builder.addAttachmentFileId(FILE_ID_2);
+		}
+
+		return builder;
+	}
+
+	static GrpcBinaryFile createBinaryFile(String fileId) {
+		return GrpcBinaryFile.newBuilder()
+				.setContentType(MediaType.TEXT_PLAIN_VALUE)
+				.setSize(FILE_CONTENT.length)
+				.setId(fileId)
+				.setName(FILE_NAME)
+				.build();
+	}
+
+	static GrpcOzgFile createFile(String fileId) {
+		return GrpcOzgFile.newBuilder()
+				.setContentType(MediaType.TEXT_PLAIN_VALUE)
+				.setSize(FILE_CONTENT.length)
+				.setId(fileId)
+				.setName(FILE_NAME)
+				.build();
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcSecurityConfiguration.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcSecurityConfiguration.java
similarity index 55%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcSecurityConfiguration.java
rename to server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcSecurityConfiguration.java
index 54330fb12fdf52683e8d62276dd710135dc17faf..6388ec39957fa149b4f1cced64de49bff3fef204 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcSecurityConfiguration.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcSecurityConfiguration.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,20 +18,12 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.mocks;
+package de.ozgcloud.antragsraum.mocks;
 
 import lombok.extern.log4j.Log4j2;
-import net.devh.boot.grpc.server.security.authentication.BearerAuthenticationReader;
-import net.devh.boot.grpc.server.security.authentication.GrpcAuthenticationReader;
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
 
 @Log4j2
 @Configuration
 public class GrpcSecurityConfiguration {
-    @Bean
-    GrpcAuthenticationReader authenticationReader() {
-        return new BearerAuthenticationReader(BearerTokenAuthenticationToken::new);
-    }
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcServerInterceptorConfiguration.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcServerInterceptorConfiguration.java
similarity index 80%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcServerInterceptorConfiguration.java
rename to server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcServerInterceptorConfiguration.java
index ba1ec28caadb23587fc61ef088570f5b5629debc..30e4f34f0bc86a0d6bdae195066821957ed3049f 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/GrpcServerInterceptorConfiguration.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/GrpcServerInterceptorConfiguration.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,7 +18,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.mocks;
+package de.ozgcloud.antragsraum.mocks;
 
 import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor;
 import org.springframework.context.annotation.Configuration;
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/JwtTokenWrapper.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/JwtTokenWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..7dc50b6d57eccfee45554787ae6ecdb03f966c48
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/JwtTokenWrapper.java
@@ -0,0 +1,61 @@
+/*
+ * 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.antragsraum.mocks;
+
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import de.ozgcloud.antragsraum.security.DefaultRole;
+import de.ozgcloud.antragsraum.security.JwtTokenProvider;
+import de.ozgcloud.antragsraum.security.User;
+import de.ozgcloud.antragsraum.security.UserTrustLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Component
+@RequiredArgsConstructor
+public class JwtTokenWrapper {
+	public static final String POSTKORB_HANDLE = "28721c6f-b78f-4d5c-a048-19fd2fc429d2";
+	public static final String USER_ID = UUID.randomUUID().toString();
+	public static final String USER_NAME = "test";
+	public static final String SAML_TOKEN_DUMMY = "saml-token-dummy";
+	private final JwtTokenProvider tokenProvider;
+
+	Authentication wrap(String accessToken) {
+		LOG.info("Got token: " + accessToken);
+		User user = User.builder().id(USER_ID)
+		  .firstName(USER_NAME)
+		  .lastName(USER_NAME)
+		  .samlToken(SAML_TOKEN_DUMMY)
+		  .username(USER_NAME)
+		  .postkorbHandle(POSTKORB_HANDLE)
+		  .trustLevel(UserTrustLevel.STORK_QAA_LEVEL_3)
+		  .tokenExpiresAt(new Date())
+		  .build();
+		return new UsernamePasswordAuthenticationToken(user.getId(), tokenProvider.generate(user), List.of(new DefaultRole()));
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/LogGrpcInterceptor.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/LogGrpcInterceptor.java
similarity index 58%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/LogGrpcInterceptor.java
rename to server/src/main/java/de/ozgcloud/antragsraum/mocks/LogGrpcInterceptor.java
index 4c13d4a42cfd7025b8ca0dcbd7131cafe4209801..da55488ee0a19d8f00abba0f79d096a9875003fc 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/mocks/LogGrpcInterceptor.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/LogGrpcInterceptor.java
@@ -1,9 +1,6 @@
 
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -22,7 +19,7 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.mocks;
+package de.ozgcloud.antragsraum.mocks;
 
 import io.grpc.Metadata;
 import io.grpc.ServerCall;
@@ -32,11 +29,10 @@ import lombok.extern.log4j.Log4j2;
 
 @Log4j2
 public class LogGrpcInterceptor implements ServerInterceptor {
-    @Override
-    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata,
-                                                                 ServerCallHandler<ReqT, RespT> serverCallHandler) {
-        LOG.info("Method: {}; Metadata: {}", serverCall.getMethodDescriptor().getFullMethodName(), metadata);
-        return serverCallHandler.startCall(serverCall, metadata);
-    }
+	@Override
+	public <R, T> ServerCall.Listener<R> interceptCall(ServerCall<R, T> serverCall, Metadata metadata, ServerCallHandler<R, T> serverCallHandler) {
+		LOG.info("Method: {}; Metadata: {}", serverCall.getMethodDescriptor().getFullMethodName(), metadata);
+		return serverCallHandler.startCall(serverCall, metadata);
+	}
 
 }
\ No newline at end of file
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockDownloadChunkedFileSender.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockDownloadChunkedFileSender.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a7704647abaf1d916cc0e74217bd43f3260a6c2
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockDownloadChunkedFileSender.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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.antragsraum.mocks;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
+
+import org.apache.commons.io.IOUtils;
+
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import io.grpc.stub.CallStreamObserver;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+class MockDownloadChunkedFileSender<T> {
+
+	private final AtomicBoolean hasUploadFile = new AtomicBoolean(true);
+	private final InputStream uploadStream;
+	private final int chunkSize;
+	private final Function<byte[], T> buildChunkRequest;
+	private T requestMetadata;
+
+	public void sendChunkTo(CallStreamObserver<T> streamObserver) {
+		if (hasUploadFile.get()) {
+			sendMetadata(streamObserver);
+			int size = sendNextChunk(streamObserver);
+			if (size < chunkSize) {
+				handleFileEndReached(streamObserver);
+			}
+		}
+	}
+
+	private void sendMetadata(CallStreamObserver<T> streamObserver) {
+		if (requestMetadata != null) {
+			streamObserver.onNext(requestMetadata);
+			requestMetadata = null;
+		}
+	}
+
+	private int sendNextChunk(CallStreamObserver<T> streamObserver) {
+		byte[] content = readFromStream();
+		var size = content.length;
+		if (size > 0) {
+			streamObserver.onNext(buildChunkRequest.apply(content));
+		}
+		return size;
+	}
+
+	private byte[] readFromStream() {
+		try {
+			return uploadStream.readNBytes(chunkSize);
+		} catch (IOException e) {
+			throw new TechnicalException("Error on sending a single chunk", e);
+		}
+	}
+
+	private void handleFileEndReached(CallStreamObserver<T> streamObserver) {
+		IOUtils.closeQuietly(uploadStream);
+		streamObserver.onCompleted();
+		hasUploadFile.getAndSet(false);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockEvent.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..23c554cebfb6b69e6af421271efcabae4de57a9a
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockEvent.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.mocks;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+
+@Builder(toBuilder = true)
+public record MockEvent(
+		@Schema(description = "The postkorbHandle",
+				name = "postkorbHandle",
+				type = "string",
+				example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2"
+		)
+		String postkorbHandle,
+		@Schema(description = "The Vorgang Id",
+				name = "vorgangId",
+				type = "string",
+				example = "abcd1c6f-b78f-4d5c-a048-19fd2fc42xxx"
+		)
+		String vorgangId,
+		@Schema(description = "Address of the OZG-Cloud instance. Should be 'static://localhost:9090' other values will produce errors!",
+				name = "address",
+				type = "string",
+				example = "static://localhost:9090"
+		)
+		String address,
+		@Schema(description = "The Vorgang title of the message send by the clerk. Optional if empty the example title is used.",
+				name = "clerkMessageTitle",
+				type = "string",
+				example = "Vorgang title <id of the reply message>"
+		)
+		String clerkMessageTitle,
+		@Schema(description = "The text of the message send by the clerk. Optional, if empty an example text is used.",
+				name = "clerkMessageText",
+				type = "string",
+				example = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr,\n sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,\\n sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.\\n\\n Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\\n Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
+		)
+		String clerkMessageText,
+
+		@Schema(description = "The timestamp when the clerk message has been send. When not set the current time and date is used",
+				name = "sendDateTime",
+				type = "String",
+				example = "2023-12-01T12:23:25.000"
+		)
+		String sendDateTime,
+		@Schema(description = "Add the test attachments to the event. Defaults to 0, which means no attachments",
+				name = "attachmentCount",
+				type = "integer",
+				example = "0")
+		int attachmentCount) {
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockLogoutSuccessHandler.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockLogoutSuccessHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..918cff52ba5773bef006f0cddaef1ca466e2813a
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockLogoutSuccessHandler.java
@@ -0,0 +1,43 @@
+/*
+ * 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.antragsraum.mocks;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+
+import java.io.IOException;
+
+@RequiredArgsConstructor
+public class MockLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
+    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+        setDefaultTargetUrl();
+        super.handle(request, response, authentication);
+    }
+
+    void setDefaultTargetUrl() {
+        super.setDefaultTargetUrl("http://localhost:8082");
+    }
+}
\ No newline at end of file
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockRedirectStrategy.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockRedirectStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce19c26f12695358a5622d3557683208eca50667
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockRedirectStrategy.java
@@ -0,0 +1,79 @@
+/*
+ * 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.antragsraum.mocks;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.DefaultRedirectStrategy;
+
+import de.ozgcloud.antragsraum.security.InMemoryUserDetailService;
+import de.ozgcloud.antragsraum.security.User;
+import de.ozgcloud.antragsraum.security.UserTrustLevel;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public class MockRedirectStrategy extends DefaultRedirectStrategy {
+	private final UserDetailsService userDetailsService;
+
+	public MockRedirectStrategy(UserDetailsService userDetailsService) {
+		super();
+		this.userDetailsService = userDetailsService;
+	}
+
+	private String onetimeToken;
+
+	public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url, Authentication authentication) throws IOException {
+		onetimeToken = UUID.randomUUID().toString();
+		if (authentication instanceof UsernamePasswordAuthenticationToken userToken) {
+			var authUser = (User) userToken.getPrincipal();
+			var user = authUser.toBuilder()
+			  .postkorbHandle(JwtTokenWrapper.POSTKORB_HANDLE)
+			  .trustLevel(UserTrustLevel.STORK_QAA_LEVEL_3)
+			  .refreshCode(RandomStringUtils.randomAlphanumeric(64))
+			  .samlToken(JwtTokenWrapper.SAML_TOKEN_DUMMY).build();
+			((InMemoryUserDetailService) userDetailsService).addUser(onetimeToken, user);
+		} else {
+			LOG.error("Invalid authentication received. Is {} expected {}", authentication.getClass().getName(),
+			  UsernamePasswordAuthenticationToken.class.getName());
+		}
+
+		sendRedirect(request, response, url);
+	}
+
+	@Override
+	public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
+		String redirectUrl = this.calculateRedirectUrl(request.getContextPath(), url);
+		redirectUrl = redirectUrl + "?code=" + onetimeToken;
+		redirectUrl = response.encodeRedirectURL(redirectUrl);
+
+		LOG.debug("Redirecting to {}", redirectUrl);
+
+		response.sendRedirect(redirectUrl);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockSuccessHandler.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockSuccessHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ec1fc8e5c06a1ba45af02e4ac7a6347c1f3e775
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/MockSuccessHandler.java
@@ -0,0 +1,93 @@
+/*
+ * 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.antragsraum.mocks;
+
+import de.ozgcloud.antragsraum.security.DefaultRole;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.WebAttributes;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+@Log4j2
+public class MockSuccessHandler implements AuthenticationSuccessHandler {
+    private final MockRedirectStrategy redirectStrategy;
+
+    private final Map<String, String> roleTargetUrlMap = new HashMap<>();
+
+    public MockSuccessHandler(final UserDetailsService userService) {
+        super();
+        redirectStrategy = new MockRedirectStrategy(userService);
+        roleTargetUrlMap.put(DefaultRole.ROLE, "http://localhost:8082");
+    }
+
+    @Override
+    public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
+        handle(request, response, authentication);
+        clearAuthenticationAttributes(request);
+    }
+
+    protected void handle(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
+        final String targetUrl = determineTargetUrl(authentication);
+
+        if (response.isCommitted()) {
+            LOG.debug("Response has already been committed. Unable to redirect to " + targetUrl);
+            return;
+        }
+
+        redirectStrategy.sendRedirect(request, response, targetUrl, authentication);
+    }
+
+    protected String determineTargetUrl(final Authentication authentication) {
+        final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
+        for (final GrantedAuthority grantedAuthority : authorities) {
+            String authorityName = grantedAuthority.getAuthority();
+            if (roleTargetUrlMap.containsKey(authorityName)) {
+                return roleTargetUrlMap.get(authorityName);
+            }
+        }
+
+        throw new IllegalStateException("Invalid role! User is missing role " + DefaultRole.ROLE);
+    }
+
+    /**
+     * Removes temporary authentication-related data which may have been stored in the session
+     * during the authentication process.
+     */
+    protected final void clearAuthenticationAttributes(final HttpServletRequest request) {
+        final HttpSession session = request.getSession(false);
+
+        if (session == null) {
+            return;
+        }
+
+        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
+    }
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/StubDataRepository.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/StubDataRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..353b54709d58125a2e52d33726255fff8c87a3e2
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/StubDataRepository.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.mocks;
+
+import de.ozgcloud.antragsraum.common.NotFoundException;
+import de.ozgcloud.antragsraum.events.NachrichtEvent;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrage;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrageAnswer;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFile;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static de.ozgcloud.antragsraum.mocks.GrpcDataCreator.*;
+
+@Log4j2
+@Component
+@RequiredArgsConstructor
+class StubDataRepository {
+	private final Map<String, List<String>> messageStorage = new ConcurrentHashMap<>();
+	private final Map<String, GrpcRueckfrage> nachrichtStorage = new ConcurrentHashMap<>();
+	private final Map<String, GrpcUploadBinaryFileMetaData> fileStorage = new ConcurrentHashMap<>();
+	private final Map<String, byte[]> fileContentStorage = new ConcurrentHashMap<>();
+
+	public GrpcFindRueckfragenResponse getRueckfragenResponse(String postfachId) {
+		reset();
+		if (messageStorage.get(postfachId) != null) {
+			var nachrichten = messageStorage.get(postfachId).stream().map(nachrichtStorage::get).toList();
+			return GrpcFindRueckfragenResponse.newBuilder().clearRueckfrage().addAllRueckfrage(nachrichten).build();
+		} else {
+			throw new NotFoundException(GrpcFindRueckfragenResponse.class, postfachId);
+		}
+	}
+
+	void addFileContent(String fileId, byte[] content) {
+		fileContentStorage.put(fileId, content);
+	}
+
+	String addFile(GrpcUploadBinaryFileMetaData file) {
+		var fileId = RandomStringUtils.randomAlphanumeric(16);
+		fileStorage.put(fileId, file);
+		return fileId;
+	}
+
+	boolean addReply(GrpcRueckfrageAnswer answer) {
+		boolean success = true;
+
+		var reply = nachrichtStorage.get(answer.getRueckfrageId());
+		if (reply != null) {
+			var attachmentsIds = answer.getAttachmentFileIdList().stream()
+					.map(this::getUploadedAnswerAttachment)
+					.filter(Optional::isPresent)
+					.map(Optional::get)
+					.map(GrpcBinaryFile::getId)
+					.toList();
+			var filledAnswer = answer.toBuilder().addAllAttachmentFileId(attachmentsIds).build();
+
+			var replyBuilder = reply.toBuilder()
+					.addAnswers(filledAnswer)
+					.setAnsweredAt(LocalDateTime.now().toString());
+
+			nachrichtStorage.put(answer.getRueckfrageId(), replyBuilder.build());
+		} else {
+			throw new NotFoundException(GrpcRueckfrageAnswer.class, answer.getRueckfrageId());
+		}
+
+		return success;
+	}
+
+	Optional<GrpcBinaryFile> getUploadedAnswerAttachment(String id) {
+		var fileDataOpt = getFileMetaData(id);
+
+		AtomicReference<Optional<GrpcBinaryFile>> res = new AtomicReference<>(Optional.empty());
+
+		fileDataOpt.ifPresent(fileMetaData -> res.set(Optional.of(GrpcBinaryFile.newBuilder()
+				.setContentType(fileMetaData.getContentType())
+				.setName(fileMetaData.getFileName())
+				.setSize(fileMetaData.getSize())
+				.setId(id).build()))
+		);
+
+		return res.get();
+	}
+
+	Optional<GrpcUploadBinaryFileMetaData> getFileMetaData(String fileId) {
+		if (FILE_ID_1.equals(fileId)) {
+			return Optional.of(GrpcUploadBinaryFileMetaData.newBuilder()
+					.setFileName(FILE_NAME)
+					.setVorgangId(VORGANG_ID_1)
+					.setSize(FILE_CONTENT.length)
+					.setContentType(MediaType.TEXT_PLAIN_VALUE)
+					.build());
+		}
+		return Optional.ofNullable(fileStorage.get(fileId));
+	}
+
+	Optional<byte[]> getFileContent(String fileId) {
+		return Optional.ofNullable(fileContentStorage.get(fileId));
+	}
+
+
+	public void reset() {
+		messageStorage.clear();
+		fileStorage.clear();
+
+		nachrichtStorage.put(NACHRICHT_ID_1_0, getRueckfrageBuilder(VORGANG_ID_1).build());
+		messageStorage.put(POSTFACH_ID_1, List.of(NACHRICHT_ID_1_0));
+
+		nachrichtStorage.put(NACHRICHT_ID_2_0, getRueckfrageBuilder(VORGANG_ID_2).build());
+		nachrichtStorage.put(NACHRICHT_ID_2_1, getRueckfrageBuilder(VORGANG_ID_2).build());
+		messageStorage.put(POSTFACH_ID_2, List.of(NACHRICHT_ID_2_0, NACHRICHT_ID_2_1));
+	}
+
+	public void setFile(GrpcBinaryFile file, String vorgangId) {
+		fileStorage.put(file.getId(), GrpcUploadBinaryFileMetaData.newBuilder()
+				.setFileName(file.getName())
+				.setVorgangId(vorgangId)
+				.setSize(file.getSize())
+				.setContentType(file.getContentType())
+				.build());
+	}
+
+	public String addReply(GrpcRueckfrage rueckfrage, NachrichtEvent event) {
+		String key = event.postfachId() + "-" + event.address();
+		nachrichtStorage.put(key, rueckfrage);
+
+		var nachrichtenListOpt = Optional.ofNullable(messageStorage.get(event.postfachId()));
+		nachrichtenListOpt.ifPresentOrElse(nachrichtenList -> {
+			if (!nachrichtenList.contains(key)) {
+				var nachrichtenIds = new ArrayList<>(nachrichtenList);
+				nachrichtenIds.add(rueckfrage.getId());
+				messageStorage.put(event.postfachId(), nachrichtenIds);
+			}
+		}, () -> messageStorage.put(event.postfachId(), List.of(rueckfrage.getId())));
+		return rueckfrage.getId();
+	}
+
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/UploadStreamObserverStub.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/UploadStreamObserverStub.java
new file mode 100644
index 0000000000000000000000000000000000000000..b45e41898ed2bf45275f798a67bde92a46d3029f
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/UploadStreamObserverStub.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.mocks;
+
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileResponse;
+import io.grpc.stub.StreamObserver;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@Log4j2
+public class UploadStreamObserverStub implements StreamObserver<GrpcUploadBinaryFileRequest> {
+	private final StreamObserver<GrpcUploadBinaryFileResponse> responseObserver;
+	private final StubDataRepository stubDataRepository;
+	private ByteArrayOutputStream byteArrayOutput;
+	private CompletableFuture<String> fileIdFuture;
+	private String fileId;
+	private String fileName;
+
+	UploadStreamObserverStub(StreamObserver<GrpcUploadBinaryFileResponse> responseObserver, StubDataRepository stubDataRepository) {
+		this.responseObserver = responseObserver;
+		this.stubDataRepository = stubDataRepository;
+	}
+
+	@Override
+	public void onNext(GrpcUploadBinaryFileRequest fileUploadRequest) {
+		if (fileUploadRequest.hasMetadata()) {
+			LOG.info("Received file metadata: " + fileUploadRequest.getMetadata());
+			fileName = fileUploadRequest.getMetadata().getFileName();
+			fileId = stubDataRepository.addFile(fileUploadRequest.getMetadata());
+			fileIdFuture = CompletableFuture.completedFuture(fileId);
+			byteArrayOutput = new ByteArrayOutputStream();
+		} else {
+			storeFileContent(fileUploadRequest);
+		}
+	}
+
+	void storeFileContent(GrpcUploadBinaryFileRequest fileUploadRequest) {
+		try {
+			IOUtils.write(fileUploadRequest.getFileContent().toByteArray(), byteArrayOutput);
+		} catch (IOException e) {
+			throw new TechnicalException(e);
+		}
+	}
+
+	@Override
+	public void onError(Throwable t) {
+		LOG.error("GrpcLargeFileRequestObserver onError has been called", t);
+		throw new TechnicalException(t);
+	}
+
+	@Override
+	public void onCompleted() {
+		try {
+			var fileContent = byteArrayOutput.toByteArray();
+			stubDataRepository.addFileContent(fileId, fileContent);
+			var file = File.createTempFile("upload-", fileName);
+			FileUtils.writeByteArrayToFile(file, fileContent);
+			LOG.info("Saved file {}", file.getAbsoluteFile());
+			responseObserver.onNext(GrpcUploadBinaryFileResponse.newBuilder().setFileId(fileIdFuture.get(3, TimeUnit.SECONDS)).build());
+
+			byteArrayOutput.close();
+		} catch (ExecutionException | IOException | TimeoutException e) {
+			handleException(e);
+		} catch (InterruptedException ie) {
+			handleException(ie);
+			Thread.currentThread().interrupt();
+		}
+		responseObserver.onCompleted();
+
+	}
+
+	void handleException(Exception e) {
+		responseObserver.onNext(GrpcUploadBinaryFileResponse.newBuilder().build());
+		LOG.error(e.getMessage(), e);
+		throw new TechnicalException(e);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/mocks/WritingNachrichtenEventService.java b/server/src/main/java/de/ozgcloud/antragsraum/mocks/WritingNachrichtenEventService.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8e47aa96f853e8cdfe4f2eaa8b45d57265b408c
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/mocks/WritingNachrichtenEventService.java
@@ -0,0 +1,71 @@
+/*
+ * 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.antragsraum.mocks;
+
+import java.util.Objects;
+import java.util.UUID;
+
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.antragsraum.events.NachrichtEvent;
+import de.ozgcloud.antragsraum.security.User;
+import de.ozgcloud.info.InformationServiceGrpc;
+import de.ozgcloud.info.nachricht.GrpcNachricht;
+import de.ozgcloud.info.nachricht.GrpcNewNachrichtRequest;
+import de.ozgcloud.info.nachricht.NachrichtServiceGrpc;
+import lombok.extern.log4j.Log4j2;
+import net.devh.boot.grpc.client.inject.GrpcClient;
+
+@Service
+@Log4j2
+public class WritingNachrichtenEventService {
+	public static final String LOCALHOST_9090 = "static://localhost:9090";
+	@GrpcClient("info-manager")
+	private NachrichtServiceGrpc.NachrichtServiceBlockingStub nachrichtServiceBlockingStub;
+
+	@GrpcClient("info-manager")
+	private InformationServiceGrpc.InformationServiceBlockingStub informationServiceBlockingStub;
+
+	public NachrichtEvent addEvent(MockEvent event) {
+		var token =
+		  new UsernamePasswordAuthenticationToken(User.builder().samlToken("saml--token").build(), "dummy--token");
+		SecurityContextHolder.getContext().setAuthentication(token);
+
+		String listUrl = event.address() != null ? event.address() : LOCALHOST_9090;
+		String postfachId =
+		  Objects.nonNull(event.postkorbHandle()) ? event.postkorbHandle() : UUID.randomUUID().toString();
+
+		var nachricht = GrpcNachricht.newBuilder()
+		  .setNachrichtenListUrl(listUrl)
+		  .setPostfachId(postfachId)
+		  .build();
+		var res = nachrichtServiceBlockingStub.saveNewNachricht(GrpcNewNachrichtRequest.newBuilder()
+		  .setNachricht(nachricht).build());
+
+		LOG.info("Saved NachrichtEvent: {}, Result: {}", nachricht, res);
+		return NachrichtEvent.builder()
+		  .postfachId(postfachId)
+		  .address("static://localhost;9091").build();
+	}
+}
+
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/AttachmentMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/AttachmentMapper.java
similarity index 57%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/AttachmentMapper.java
rename to server/src/main/java/de/ozgcloud/antragsraum/nachricht/AttachmentMapper.java
index 04bc86ffb841026bd835d052b0afa16735623441..45e6bbca1c99b482ca81396d51f231221a40d2a4 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/AttachmentMapper.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/AttachmentMapper.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,20 +18,20 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.nachricht;
+package de.ozgcloud.antragsraum.nachricht;
 
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFile;
-import de.mgm.bup.ozg.antragsraum.attachments.OzgFile;
+import de.ozgcloud.antragsraum.attachments.OzgFile;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFile;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 class AttachmentMapper {
-    static OzgFile fromGrpcBinaryFile(GrpcBinaryFile file) {
-        return OzgFile.builder()
-                .id(file.getId())
-                .fileName(file.getName())
-                .fileSize(file.getSize())
-                .contentType(file.getContentType()).build();
-    }
+	static OzgFile fromGrpcBinaryFile(GrpcBinaryFile file) {
+		return OzgFile.builder()
+		  .id(file.getId())
+		  .fileName(file.getName())
+		  .fileSize(file.getSize())
+		  .contentType(file.getContentType()).build();
+	}
 }
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/Nachricht.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/Nachricht.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5c79f95809882df632c83ca67e96f54240e9fc2
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/Nachricht.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.List;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+import org.springframework.validation.annotation.Validated;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import de.ozgcloud.antragsraum.attachments.OzgFile;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Builder(toBuilder = true)
+@Validated
+public record Nachricht(
+  @Schema(description = "The address of the ozg-cloud instance base64 encoded", name = "id", type = "string", example = "1258ad3f46811d04010f22ab")
+  @NotBlank
+  String id,
+  @Schema(description = "The address of the ozg-cloud instance", name = "address", type = "string", example = "static://localhost:9090")
+  @NotBlank
+  String address,
+  @Schema(description = "The date the Rueckfrage has been send.")
+  @Min(0)
+  long date,
+  @Schema(description = "The title of the topic. This is the title of the Vorgang",
+	name = "topicTitle",
+	type = "string",
+	example = "Vorgang Hundesteuer Antrag"
+  )
+  @Size(max = 2048, message = "title too long")
+  String topicTitle,
+  @Size(max = 500000, message = "message too long")
+  String message,
+  @Schema(description = "The id of the postfach. For BayernID this is the PostkorbHandle",
+	name = "postfachId",
+	type = "string",
+	example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2"
+  )
+  @Size(max = 64, message = "postfach id to long")
+  String postfachId,
+  @Schema(description = "The id of the Vorgang the Rueckfrage belongs to",
+	name = "vorgangId",
+	type = "string",
+	example = "8523ad3f46811d04010f6654"
+  )
+  @Size(max = 64, message = "vongang id id to long")
+  String vorgangId,
+  @Schema(description = "The reply option, not used right now always set 'ALLOWED'",
+	name = "vorgangId",
+	type = "ReplyOption",
+	example = "ALLOWED"
+  )
+  ReplyOption replyOption,
+  @Schema(description = "The list of the attached files.")
+  List<OzgFile> attachments,
+  @Schema(hidden = true)
+  List<ReplyNachricht> replyNachrichten,
+  @Schema(description = "The trust level of the Rueckfrage",
+	name = "trustLevel",
+	type = "NachrichtTrustLevel"
+  )
+  NachrichtTrustLevel trustLevel) {
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeader.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeader.java
new file mode 100644
index 0000000000000000000000000000000000000000..483e142833870bdc6f38a09e4c2ff6bb320f8785
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeader.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+import org.springframework.validation.annotation.Validated;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Builder(toBuilder = true)
+@Validated
+public record NachrichtHeader(
+  @Schema(description = "The address of the ozg-cloud instance base64 encoded", name = "id", type = "string", example = "1258ad3f46811d04010f22ab")
+  @NotBlank
+  String id,
+  @Schema(description = "The address of the ozg-cloud instance", name = "address", type = "string", example = "static://localhost:9090")
+  @NotBlank
+  String address,
+  @Schema(description = "The date the Rueckfrage has been send.")
+  @Min(0)
+  long date,
+  @Schema(description = "The title of the topic. This is the title of the Vorgang",
+	name = "topicTitle",
+	type = "string",
+	example = "Vorgang Hundesteuer Antrag"
+  )
+  @Size(max = 2048, message = "title too long")
+  String topicTitle,
+  @Schema(description = "The id of the postfach. For BayernID this is the PostkorbHandle",
+	name = "postfachId",
+	type = "string",
+	example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2"
+  )
+  @Size(max = 64, message = "postfach id to long")
+  String postfachId,
+  @Schema(description = "The id of the Vorgang the Rueckfrage belongs to",
+	name = "vorgangId",
+	type = "string",
+	example = "8523ad3f46811d04010f6654"
+  )
+  @Size(max = 64, message = "vongang id id to long")
+  String vorgangId,
+  @Schema(description = "The reply option, not used right now always set 'ALLOWED'",
+	name = "vorgangId",
+	type = "ReplyOption",
+	example = "ALLOWED"
+  )
+  ReplyOption replyOption,
+  @Schema(description = "The date on which the Rueckfrage was last answered")
+  @Min(0)
+  Long lastAnsweredDate,
+  @Schema(description = "The trust level of the Rueckfrage",
+	name = "trustLevel",
+	type = "NachrichtTrustLevel"
+  )
+  NachrichtTrustLevel trustLevel) {
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeaderMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeaderMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e732e16896eda79c596aeb63faa712579ed7f57
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeaderMapper.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+class NachrichtHeaderMapper {
+	static NachrichtHeader fromNachricht(Nachricht nachricht) {
+		var builder = NachrichtHeader.builder()
+		  .vorgangId(nachricht.vorgangId())
+		  .id(nachricht.id())
+		  .address(nachricht.address())
+		  .topicTitle(nachricht.topicTitle())
+		  .date(nachricht.date())
+		  .lastAnsweredDate(getLatestReplyDate(nachricht))
+		  .trustLevel(NachrichtTrustLevel.LOW);
+
+		return builder.build();
+	}
+
+	static Long getLatestReplyDate(Nachricht nachricht) {
+		return nachricht.replyNachrichten().stream().map(ReplyNachricht::date).max(Long::compare).orElse(null);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..4918c92b2eef43eedd29a1e2dd4a6fb1fb3ccdaa
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtMapper.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.antragsraum.attachments.FileRemoteService;
+import de.ozgcloud.antragsraum.attachments.OzgFile;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrage;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrageAnswer;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Service
+@RequiredArgsConstructor
+class NachrichtMapper {
+	private final @NonNull FileRemoteService fileRemoteService;
+
+	Nachricht fromGrpcRueckfrage(GrpcRueckfrage grpcRueckfrage, String address) {
+		var builder = Nachricht.builder()
+		  .vorgangId(grpcRueckfrage.getVorgangId())
+		  .id(grpcRueckfrage.getId())
+		  .address(address)
+		  .topicTitle(grpcRueckfrage.getVorgangName())
+		  .message(grpcRueckfrage.getText())
+		  .replyNachrichten(fromGrpcRueckfrageAnswer(grpcRueckfrage, address))
+		  .date(getUnixTimestamp(grpcRueckfrage.getSentAt()))
+		  .attachments(getAttachments(grpcRueckfrage.getAttachmentFileIdList(), address))
+		  .trustLevel(NachrichtTrustLevel.LOW);
+
+		return builder.build();
+	}
+
+	private List<OzgFile> getAttachments(List<String> fileIds, String address) {
+		return fileIds.stream().filter(StringUtils::isNotEmpty).map(fileId -> fileRemoteService.getFile(fileId, address)).filter(Objects::nonNull)
+		  .toList();
+	}
+
+	private List<ReplyNachricht> fromGrpcRueckfrageAnswer(GrpcRueckfrage rueckfrage, String address) {
+		return rueckfrage.getAnswersList().stream()
+		  .map(answer -> {
+			  var builder = ReplyNachricht.builder()
+				.id(rueckfrage.getId())
+				.address(address)
+				.message(answer.getAnswerText())
+				.date(getUnixTimestamp(rueckfrage.getAnsweredAt()))
+				.attachments(getAttachments(answer.getAttachmentFileIdList(), address));
+			  return builder.build();
+		  }).toList();
+	}
+
+	private long getUnixTimestamp(String dateString) {
+		long timestamp = 0;
+		try {
+			timestamp = LocalDateTime.parse(dateString).toEpochSecond(ZoneOffset.UTC);
+		} catch (DateTimeException e) {
+			LOG.warn("Invalid send date [{}] received for Nachricht. Setting it to 0 (=1970-01-01)", dateString);
+		}
+		return timestamp;
+	}
+
+	GrpcRueckfrageAnswer toGrpcRueckfrageAnswer(ReplyNachricht nachricht) {
+		var builder = GrpcRueckfrageAnswer.newBuilder()
+		  .setRueckfrageId(nachricht.id())
+		  .setAnswerText(nachricht.message());
+
+		if (Objects.nonNull(nachricht.attachments())) {
+			builder.addAllAttachmentFileId(nachricht.attachments().stream()
+			  .map(OzgFile::id).toList());
+		}
+		return builder.build();
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtProperties.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtProperties.java
similarity index 61%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtProperties.java
rename to server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtProperties.java
index f31017a8531fbb045ad245308b55229d5d2483a9..30ce7e795bd9718a33123a13126b8a7e064190f2 100755
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtProperties.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtProperties.java
@@ -1,53 +1,51 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import lombok.Getter;
-import lombok.Setter;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-import java.time.Duration;
-
-@Getter
-@Setter
-@Configuration
-@ConfigurationProperties(NachrichtProperties.PREFIX)
-class NachrichtProperties {
-    static final String PREFIX = "ozgcloud.nachricht";
-    // Send timeout as ISO-8601 duration format PnDTnHnMn.nS
-    private String sendTimeout = "PT60S";
-    // Send intervall as ISO-8601 duration format PnDTnHnMn.nS
-    private String sendPollInterval = "PT2S";
-
-    Duration getTimeoutDuration() {
-        return Duration.parse(sendTimeout);
-    }
-
-    Duration getSendPollIntervalDuration() {
-        return Duration.parse(sendPollInterval);
-    }
-
-
-}
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.time.Duration;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Configuration
+@ConfigurationProperties(NachrichtProperties.PREFIX)
+class NachrichtProperties {
+	static final String PREFIX = "ozgcloud.nachricht";
+	// Send timeout as ISO-8601 duration format PnDTnHnMn.nS
+	private String sendTimeout = "PT60S";
+	// Send intervall as ISO-8601 duration format PnDTnHnMn.nS
+	private String sendPollInterval = "PT2S";
+
+	Duration getTimeoutDuration() {
+		return Duration.parse(sendTimeout);
+	}
+
+	Duration getSendPollIntervalDuration() {
+		return Duration.parse(sendPollInterval);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtTrustLevel.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtTrustLevel.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d9f18ffcbf151007a88ac3dc91a9232c53c01dd
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtTrustLevel.java
@@ -0,0 +1,36 @@
+/*
+ * 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.antragsraum.nachricht;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public enum NachrichtTrustLevel {
+	LOW("Niedrig"),
+	SUBSTANTIAL("Substantiell"),
+	HIGH("Hoch");
+
+	private final String value;
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenController.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenController.java
new file mode 100644
index 0000000000000000000000000000000000000000..76007db0d216682fdf65106a2a423535ef2be5e7
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.List;
+
+import jakarta.validation.Valid;
+
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+@RestController
+@RequestMapping(NachrichtenController.PATH)
+@RequiredArgsConstructor
+@SecurityRequirement(name = "security_auth")
+public class NachrichtenController {
+	public static final String PATH = "/api";
+
+	private final @NonNull NachrichtenService service;
+
+	@Operation(summary = "Load all Nachrichten of a Postfach")
+	@GetMapping(value = "/nachrichten/{postfachId}", produces = MediaType.APPLICATION_JSON_VALUE)
+	public List<TopicHeader> getTopicHeadersOfPostfach(
+	  @Parameter(description = "The id of the postfach", example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2") @PathVariable String postfachId) {
+		return service.getTopicHeadersOfPostfach(postfachId);
+	}
+
+	@Operation(summary = "Load all Nachrichten of a specific topic of a Postfach")
+	@GetMapping(value = "/nachrichten/{postfachId}/{rueckfrageId}", produces = MediaType.APPLICATION_JSON_VALUE)
+	public Topic getTopicOfPostfach(
+	  @Parameter(description = "The id of the postfach", example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2") @PathVariable String postfachId,
+	  @Parameter(description = "The id of the topic", example = "1258ad3f46811d04010f22ab") @PathVariable String rueckfrageId) {
+		return service.getTopicOfPostfach(postfachId, rueckfrageId);
+	}
+
+	@Operation(summary = "Send the reply to the Rueckfrage into the ozg cloud")
+	@PutMapping(value = "/nachricht", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+	public ReplyNachricht sendNachricht(
+	  @Parameter(description = "The Nachricht content as JSON") @RequestBody @Valid ReplyNachricht nachricht) {
+		return service.sendRueckfrageAnswer(nachricht);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenGrpcClient.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenGrpcClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..a20fa73c627b1690ccddc508d2bb3c8e4eed3cf2
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenGrpcClient.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import org.springframework.stereotype.Component;
+
+import de.ozgcloud.antragsraum.common.ChannelPropertiesFactory;
+import de.ozgcloud.nachrichten.antragraum.AntragraumServiceGrpc;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerResponse;
+import de.ozgcloud.vorgang.grpc.command.CommandServiceGrpc;
+import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
+import de.ozgcloud.vorgang.grpc.command.GrpcGetCommandRequest;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
+import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+
+@Log4j2
+@Component
+@RequiredArgsConstructor
+public class NachrichtenGrpcClient {
+	private final @NonNull GlobalClientInterceptorRegistry registry;
+	private final @NonNull ChannelPropertiesFactory channelPropertiesFactory;
+
+	GrpcFindRueckfragenResponse getFindRueckfragen(GrpcFindRueckfragenRequest request, String address) {
+		try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
+			var channel = channelFactory.createChannel(address);
+			var antragsServiceBlockingStub = AntragraumServiceGrpc.newBlockingStub(channel);
+
+			return getGrpcFindRueckfragenResponse(request, antragsServiceBlockingStub);
+		}
+	}
+
+	GrpcFindRueckfragenResponse getGrpcFindRueckfragenResponse(GrpcFindRueckfragenRequest request,
+	  AntragraumServiceGrpc.AntragraumServiceBlockingStub antragraumServiceBlockingStub) {
+		return antragraumServiceBlockingStub.findRueckfragen(request);
+	}
+
+	GrpcSendRueckfrageAnswerResponse answerRueckfrage(GrpcSendRueckfrageAnswerRequest request, String address) {
+		try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
+			var channel = channelFactory.createChannel(address);
+			var antragsServiceBlockingStub = AntragraumServiceGrpc.newBlockingStub(channel);
+
+			return getAnswerResponse(request, antragsServiceBlockingStub);
+		}
+	}
+
+	GrpcSendRueckfrageAnswerResponse getAnswerResponse(GrpcSendRueckfrageAnswerRequest request,
+	  AntragraumServiceGrpc.AntragraumServiceBlockingStub antragraumServiceBlockingStub) {
+		return antragraumServiceBlockingStub.sendRueckfrageAnswer(request);
+	}
+
+	GrpcCommand getCommand(String address, String commandId) {
+		try (GrpcChannelFactory channelFactory = getChannelFactory(address)) {
+			var channel = channelFactory.createChannel(address);
+			var commandServiceBlockingStub = CommandServiceGrpc.newBlockingStub(channel);
+
+			return getGrpcCommand(commandServiceBlockingStub, GrpcGetCommandRequest.newBuilder().setId(commandId).build());
+		}
+	}
+
+	GrpcCommand getGrpcCommand(CommandServiceGrpc.CommandServiceBlockingStub commandServiceBlockingStub,
+	  GrpcGetCommandRequest request) {
+		return commandServiceBlockingStub.getCommand(request);
+	}
+
+	GrpcChannelFactory getChannelFactory(String channelAddress) {
+		return de.ozgcloud.antragsraum.common.GrpcChannelFactory.getChannelFactory(
+		  channelPropertiesFactory.getGrpcChannelsProperties(channelAddress), registry);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteService.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4c443f65a5f9f51c7b8d86e84b3ce7947524bcc
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteService.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.common.SendTimeoutException;
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerResponse;
+import io.grpc.StatusRuntimeException;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Service
+@Log4j2
+@RequiredArgsConstructor
+class NachrichtenRemoteService {
+	public static final String STATUS_FINISHED = "FINISHED";
+
+	private final @NonNull NachrichtenGrpcClient grpcClient;
+	private final @NonNull NachrichtProperties properties;
+	private final @NonNull ContextService contextService;
+	private final @NonNull NachrichtMapper nachrichtMapper;
+
+	List<Nachricht> findRueckfragen(String postfachId, String address) {
+		try {
+			var rfBuilder = GrpcFindRueckfragenRequest.newBuilder()
+			  .setSamlToken(contextService.getSamlResponse())
+			  .setPostfachId(postfachId);
+			if (Objects.nonNull(contextService.getSamlResponse())) {
+				rfBuilder.setSamlToken(contextService.getSamlResponse());
+			}
+			var reply = grpcClient.getFindRueckfragen(rfBuilder.build(), address);
+
+			return reply.getRueckfrageList().stream()
+			  .map(rueckfrage -> nachrichtMapper.fromGrpcRueckfrage(rueckfrage, address)).toList();
+		} catch (StatusRuntimeException e) {
+			LOG.error("Error loading nachricht of postfach " + postfachId, e);
+		}
+
+		return List.of();
+	}
+
+	boolean sendAnswer(ReplyNachricht nachricht, String address) {
+		GrpcSendRueckfrageAnswerResponse response = grpcClient.answerRueckfrage(
+		  GrpcSendRueckfrageAnswerRequest.newBuilder()
+			.setAnswer(nachrichtMapper.toGrpcRueckfrageAnswer(nachricht))
+			.setSamlToken(contextService.getSamlResponse())
+			.build(), address);
+
+		return waitUntilCommandIsFinished(address, response.getCommandId());
+	}
+
+	boolean waitUntilCommandIsFinished(String address, String commandId) {
+		var auth = SecurityContextHolder.getContext().getAuthentication();
+		ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+		Future<Boolean> future = executor.submit(() -> checkCommandIsFinished(address, commandId, auth));
+		try {
+			return getSendResult(future);
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			throw new TechnicalException(e);
+		} catch (ExecutionException e) {
+			throw new TechnicalException(e);
+		} catch (TimeoutException e) {
+			throw new SendTimeoutException(commandId, e);
+		} finally {
+			try {
+				executor.shutdown();
+			} catch (Exception e) {
+				LOG.error("Error shutting down executor. Closing it", e);
+				executor.close();
+			}
+		}
+	}
+
+	Boolean checkCommandIsFinished(String address, String commandId, Authentication auth) throws InterruptedException {
+		SecurityContextHolder.getContext().setAuthentication(auth);
+
+		boolean notFinished = true;
+		while (notFinished) {
+			var resCommand = grpcClient.getCommand(address, commandId);
+			notFinished = !STATUS_FINISHED.equals(resCommand.getStatus());
+
+			TimeUnit.MILLISECONDS.sleep(properties.getSendPollIntervalDuration().toMillis());
+		}
+
+		return true;
+	}
+
+	Boolean getSendResult(Future<Boolean> future) throws InterruptedException, ExecutionException, TimeoutException {
+		return future.get(properties.getTimeoutDuration().toMillis(), TimeUnit.MILLISECONDS);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenService.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenService.java
new file mode 100644
index 0000000000000000000000000000000000000000..da45fe6538dbf0046628a02b8778c8fc8658471c
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.antragsraum.common.InsufficientTrustLevelException;
+import de.ozgcloud.antragsraum.events.NachrichtEvent;
+import de.ozgcloud.antragsraum.events.NachrichtEventService;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Service
+@RequiredArgsConstructor
+class NachrichtenService {
+	static final String TOPIC_LINK_RELATIONSHIP_NAME = "topic";
+
+	private final @NonNull NachrichtEventService nachrichtEventService;
+	private final @NonNull NachrichtenRemoteService nachrichtenRemoteService;
+	private final @NonNull NachrichtenTrustLevelService nachrichtenTrustLevelService;
+
+	List<TopicHeader> getTopicHeadersOfPostfach(String postfachId) {
+		var topicHeaders = TopicHeaderMapper.fromNachrichten(getRueckfragenOfPostfach(postfachId));
+
+		return linkTopicHeaders(topicHeaders, postfachId);
+	}
+
+	private List<TopicHeader> linkTopicHeaders(List<TopicHeader> topicHeaders, String postfachId) {
+		return topicHeaders.stream().peek(topicHeader -> {
+			topicHeader.add(linkTo(NachrichtenController.class).withSelfRel());
+
+			if (nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(topicHeader.getTrustLevel())) {
+				topicHeader.add(
+				  linkTo(methodOn(NachrichtenController.class).getTopicOfPostfach(postfachId, topicHeader.getClerkMessage().id())).withRel(
+					TOPIC_LINK_RELATIONSHIP_NAME));
+			}
+		}).toList();
+	}
+
+	Topic getTopicOfPostfach(String postfachId, String rueckfrageId) {
+		var topic = TopicMapper.fromNachricht(getRueckfrageById(postfachId, rueckfrageId));
+
+		if (!nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(topic.getTrustLevel())) {
+			throw new InsufficientTrustLevelException(rueckfrageId);
+		}
+
+		return topic;
+	}
+
+	private Nachricht getRueckfrageById(String postfachId, String rueckfrageId) {
+		return getRueckfragenOfPostfach(postfachId).stream().filter(nachricht -> nachricht.id().equals(rueckfrageId)).findFirst().orElseThrow();
+	}
+
+	Collection<Nachricht> getRueckfragenOfPostfach(String postfachId) {
+		var events = nachrichtEventService.getNachrichtEventsOfPostfachId(postfachId);
+
+		return events.stream().map(this::getNachrichtenOfEvent).flatMap(List::stream).toList();
+	}
+
+	private List<Nachricht> getNachrichtenOfEvent(final NachrichtEvent event) {
+		return nachrichtenRemoteService.findRueckfragen(event.postfachId(), event.address());
+	}
+
+	ReplyNachricht sendRueckfrageAnswer(final ReplyNachricht nachricht) {
+		nachrichtenRemoteService.sendAnswer(nachricht, nachricht.address());
+
+		return nachricht;
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenTrustLevelService.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenTrustLevelService.java
new file mode 100644
index 0000000000000000000000000000000000000000..46da0380e57f3905fa7c814dfde813163afdd0b7
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/NachrichtenTrustLevelService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.antragsraum.nachricht;
+
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.session.SessionAuthenticationException;
+import org.springframework.stereotype.Service;
+
+import de.ozgcloud.antragsraum.security.User;
+import de.ozgcloud.antragsraum.security.UserTrustLevel;
+
+@Service
+public class NachrichtenTrustLevelService {
+	boolean nachrichtMatchesUserTrustLevel(NachrichtTrustLevel nachrichtTrustLevel) {
+		var userTrustLevel = getCurrentUser().getTrustLevel();
+		if (userTrustLevel == null) {
+			return false;
+		}
+
+		if (nachrichtTrustLevel == null || NachrichtTrustLevel.LOW.equals(nachrichtTrustLevel)) {
+			return true;
+		}
+
+		if (UserTrustLevel.STORK_QAA_LEVEL_4.equals(userTrustLevel)) {
+			return true;
+		}
+
+		if (UserTrustLevel.STORK_QAA_LEVEL_3.equals(userTrustLevel)) {
+			return NachrichtTrustLevel.SUBSTANTIAL.equals(nachrichtTrustLevel);
+		}
+
+		return false;
+	}
+
+	private User getCurrentUser() {
+		var authentication = SecurityContextHolder.getContext().getAuthentication();
+		if (authentication != null && authentication.getPrincipal() instanceof UserDetails) {
+			return (User) authentication.getPrincipal();
+		}
+
+		throw new SessionAuthenticationException("Current user is null");
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/ReplyNachricht.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/ReplyNachricht.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffa148c2522be71b283d71f888f59db9f9e7a049
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/ReplyNachricht.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.List;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Size;
+
+import org.springframework.validation.annotation.Validated;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import de.ozgcloud.antragsraum.attachments.OzgFile;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Builder(toBuilder = true)
+@Validated
+public record ReplyNachricht(
+  @Schema(description = "The id of the postfach",
+	name = "postfachId",
+	type = "string",
+	example = "28721c6f-b78f-4d5c-a048-19fd2fc429d2"
+  )
+  String postfachId,
+  @Schema(description = "The address of the OZG-Cloud instanz the reply belongs to",
+	name = "address",
+	type = "string",
+	example = "static://utopia.sh.ozg-cloud.de:9090"
+  )
+  String address,
+  @Schema(description = "The encoded address of the OZG-Cloud instanz the reply belongs to",
+	name = "id",
+	type = "string",
+	example = "c3RhdGljOi8vdXRvcGlhLnNoLm96Zy1jbG91ZC5kZTo5MDkw"
+  )
+  @NotEmpty
+  String id,
+  @Schema(description = "The answer to the rueckfrage.",
+	name = "message",
+	type = "string",
+	example = "Im Anhang sind die Dokumente"
+  )
+  @Size(max = 500000, message = "message too long")
+  String message,
+  @Schema(description = "The answered date. Only set when displaying an already answered Rueckfrage. Will be set by the OZG-Cloud")
+  @Min(0)
+  long date,
+  @Schema(description = "The list of the attached file ids. Only the file id is used when sending the reply, The other values are set by the backend")
+  List<OzgFile> attachments) {
+}
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyOption.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/ReplyOption.java
similarity index 78%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyOption.java
rename to server/src/main/java/de/ozgcloud/antragsraum/nachricht/ReplyOption.java
index b252fb43102b6ecf727e775ea725e0c7d15697e2..855ba1e718a1c7b8c1a59b1f4d650ab62f087c38 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyOption.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/ReplyOption.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,8 +20,8 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.nachricht;
+package de.ozgcloud.antragsraum.nachricht;
 
 public enum ReplyOption {
-    FORBIDDEN, ALLOWED, UNDEFINED
+	FORBIDDEN, ALLOWED, UNDEFINED
 }
diff --git a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/Topic.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/Topic.java
similarity index 50%
rename from ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/Topic.java
rename to server/src/main/java/de/ozgcloud/antragsraum/nachricht/Topic.java
index 892e1572a272bb90b8f9352a19be476fccbb5b83..4d685bede737e16af80aaf0716ee6192b77bcedc 100644
--- a/ozg-antragsraum-server/src/main/java/de/mgm/bup/ozg/antragsraum/nachricht/Topic.java
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/Topic.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,29 +20,36 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.nachricht;
+package de.ozgcloud.antragsraum.nachricht;
+
+import java.util.List;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Builder;
-
-import java.util.List;
+import lombok.Getter;
 
 @Builder
-public record Topic(
-        @Schema(description = "The title of the topic. This is the title of the Vorgang",
-                name = "topicTitle",
-                type = "string",
-                example = "Vorgang Hundesteuer Antrag"
-        )
-        String topicTitle,
-        @Schema(description = "The Rueckfrage send by the clerk",
-                name = "clerkMessage",
-                type = "Nachricht"
-        )
-        Nachricht clerkMessage,
-        @Schema(description = "The reply to the Rueckfrage",
-                name = "userMessage",
-                type = "ReplyNachricht"
-        )
-        List<ReplyNachricht> userMessages) {
+@Getter
+public class Topic {
+	@Schema(description = "The title of the topic. This is the title of the Vorgang",
+	  name = "topicTitle",
+	  type = "string",
+	  example = "Vorgang Hundesteuer Antrag"
+	)
+	String topicTitle;
+	@Schema(description = "The Rueckfrage sent by the clerk",
+	  name = "clerkMessage",
+	  type = "Nachricht"
+	)
+	Nachricht clerkMessage;
+	@Schema(description = "The trust level of the Rueckfrage sent by the clerk",
+	  name = "trustLevel",
+	  type = "NachrichtTrustLevel"
+	)
+	NachrichtTrustLevel trustLevel;
+	@Schema(description = "The reply to the Rueckfrage",
+	  name = "userMessage",
+	  type = "ReplyNachricht"
+	)
+	List<ReplyNachricht> userMessages;
 }
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicHeader.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicHeader.java
new file mode 100644
index 0000000000000000000000000000000000000000..0229a5d7beb47f399d19bd33254f62799e064cfa
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicHeader.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import org.springframework.hateoas.RepresentationModel;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Getter;
+
+@Builder
+@Getter
+public class TopicHeader extends RepresentationModel<TopicHeader> {
+	@Schema(description = "The title of the topic. This is the title of the Vorgang",
+	  name = "topicTitle",
+	  type = "string",
+	  example = "Vorgang Hundesteuer Antrag"
+	)
+	String topicTitle;
+	@Schema(description = "The header of the Rueckfrage sent by the clerk",
+	  name = "clerkMessage",
+	  type = "NachrichtHeader"
+	)
+	NachrichtHeader clerkMessage;
+	@Schema(description = "The trust level of the Rueckfrage sent by the clerk",
+	  name = "trustLevel",
+	  type = "NachrichtTrustLevel"
+	)
+	NachrichtTrustLevel trustLevel;
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicHeaderMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicHeaderMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..f20f91e4dc4d1a73b2348aa0e6473cc83721bde5
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicHeaderMapper.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.Collection;
+import java.util.List;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+class TopicHeaderMapper {
+	static List<TopicHeader> fromNachrichten(Collection<Nachricht> nachrichten) {
+		return nachrichten.stream().map(TopicHeaderMapper::fromNachricht).toList();
+	}
+
+	private static TopicHeader fromNachricht(Nachricht nachricht) {
+		return TopicHeader.builder()
+		  .topicTitle(nachricht.topicTitle())
+		  .clerkMessage(NachrichtHeaderMapper.fromNachricht(nachricht))
+		  .trustLevel(NachrichtTrustLevel.LOW)
+		  .build();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..da6ac2c5c5194d36c2eec5cac04b4851cc4b8fa2
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/nachricht/TopicMapper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.List;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+class TopicMapper {
+	static Topic fromNachricht(Nachricht nachricht) {
+		var clerkMessage = nachricht.toBuilder().replyNachrichten(null).build();
+
+		return Topic.builder()
+		  .topicTitle(nachricht.topicTitle())
+		  .clerkMessage(clerkMessage)
+		  .trustLevel(NachrichtTrustLevel.LOW)
+		  .userMessages(createUserNachricht(nachricht.replyNachrichten()))
+		  .build();
+	}
+
+	private static List<ReplyNachricht> createUserNachricht(List<ReplyNachricht> replyNachrichten) {
+		return replyNachrichten.stream().map(replyNachricht -> ReplyNachricht.builder()
+		  .id(replyNachricht.id())
+		  .address(replyNachricht.address())
+		  .message(replyNachricht.message())
+		  .date(replyNachricht.date())
+		  .attachments(replyNachricht.attachments())
+		  .build()).toList();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/AntragsraumLogoutSuccessHandler.java b/server/src/main/java/de/ozgcloud/antragsraum/security/AntragsraumLogoutSuccessHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..13194163562cdf160a387f750ffa3aa21482b202
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/AntragsraumLogoutSuccessHandler.java
@@ -0,0 +1,46 @@
+/*
+ * 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.antragsraum.security;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+
+import java.io.IOException;
+import java.util.Objects;
+
+@RequiredArgsConstructor
+public class AntragsraumLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
+	private final UserDetailsService userDetailService;
+
+	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+		if (Objects.nonNull(authentication) && authentication.getPrincipal() instanceof User user) {
+			((InMemoryUserDetailService) userDetailService).logout(user);
+		}
+
+		super.handle(request, response, authentication);
+	}
+}
\ No newline at end of file
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/AntragsraumProperties.java b/server/src/main/java/de/ozgcloud/antragsraum/security/AntragsraumProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..622d80609f1624c358abcb81b3b404c66efb342c
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/AntragsraumProperties.java
@@ -0,0 +1,35 @@
+/*
+ * 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.antragsraum.security;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "ozgcloud.antragsraum")
+@Getter
+@Setter
+public class AntragsraumProperties {
+    private String[] authOrigins;
+    private String[] apiOrigins;
+    private String[] otherOrigins;
+    private String logoutSuccessUrl = "http://localhost:8082/?logout";
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/AuthCode.java b/server/src/main/java/de/ozgcloud/antragsraum/security/AuthCode.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ce746f9791cd6eb7b90d0337fabeb91ca6fee79
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/AuthCode.java
@@ -0,0 +1,24 @@
+/*
+ * 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.antragsraum.security;
+
+public record AuthCode(String code) {
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/AuthenticationController.java b/server/src/main/java/de/ozgcloud/antragsraum/security/AuthenticationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb7097dad196cffd117d2a9dcbf802bae666b2f1
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/AuthenticationController.java
@@ -0,0 +1,69 @@
+/*
+ * 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.antragsraum.security;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/auth")
+public class AuthenticationController {
+    private final InMemoryUserDetailService userDetailsService;
+    private final JwtTokenProvider tokenProvider;
+
+    @PostMapping(path = "/authenticate", produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<String> login(@RequestBody AuthCode data) {
+        String token;
+        try {
+            token = createJwt(data.code());
+        } catch (Exception e) {
+            throw new SecurityException("Error creating jwt: ", e);
+        }
+        return ResponseEntity.ok().body(token);
+    }
+
+    private String createJwt(String code) {
+        return tokenProvider.generate(userDetailsService.getUserByCode(code));
+    }
+
+    @PostMapping("/refresh")
+    public ResponseEntity<String> refresh(@RequestBody AuthCode code) {
+        var userOptional = userDetailsService.getUser(code);
+
+        return userOptional.map(this::tokenRefreshResponse).orElse(ResponseEntity.status(HttpStatus.FORBIDDEN).build());
+    }
+
+    ResponseEntity<String> tokenRefreshResponse(User user) {
+        return ResponseEntity.ok().body(tokenProvider.generate(userDetailsService.updateRefreshCodeOf(user)));
+    }
+
+    @GetMapping(path = "/logout")
+    public ResponseEntity<String> logout(@AuthenticationPrincipal User user) {
+        userDetailsService.logout(user);
+
+        return ResponseEntity.ok("Success");
+    }
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/BayernIdProperties.java b/server/src/main/java/de/ozgcloud/antragsraum/security/BayernIdProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..46b8ac67857cf2bb0aacfb495f60377f643e23da
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/BayernIdProperties.java
@@ -0,0 +1,39 @@
+/*
+ * 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.antragsraum.security;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@ConfigurationProperties(prefix = "ozgcloud.bayernid")
+@Getter
+@Setter
+public class BayernIdProperties {
+	private String organizationDisplayName;
+	private Map<String, String> authnMethods;
+	private List<String> requestedAttributeUrns;
+	private String redirectUrl;
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/BayernIdSaml2Extension.java b/server/src/main/java/de/ozgcloud/antragsraum/security/BayernIdSaml2Extension.java
new file mode 100644
index 0000000000000000000000000000000000000000..384fc1a0c7c4f151f323824c29b703bf6b8e6474
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/BayernIdSaml2Extension.java
@@ -0,0 +1,119 @@
+/*
+ * 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.antragsraum.security;
+
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.core.xml.schema.impl.XSAnyBuilder;
+import org.opensaml.saml.saml2.core.Extensions;
+import org.opensaml.saml.saml2.core.impl.ExtensionsBuilder;
+import org.springframework.stereotype.Component;
+
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+@Component
+@RequiredArgsConstructor
+public class BayernIdSaml2Extension {
+	@NonNull
+	private final BayernIdProperties properties;
+
+	static final String VERSION_LOCAL_PART = "Version";
+	private static final String VERSION_VALUE = "2";
+	static final String NAMESPACE_PREFIX = "akdb";
+	static final String NAMESPACE_URI = "https://www.akdb.de/request/2018/09";
+	static final String CLASSIC_UI_PREFIX = "classic-ui";
+	static final String CLASSIC_UI_URI = "https://www.akdb.de/request/2018/09/classic-ui/v1";
+	static final String AUTHN_METHODS_LOCAL_NAME = "AuthnMethods";
+	static final String AUTHENTICATION_REQUEST_LOCAL_NAME = "AuthenticationRequest";
+	static final String ENABLED_LOCAL_NAME = "Enabled";
+	static final String REQUESTED_ATTRIBUTES_LOCAL_NAME = "RequestedAttributes";
+	private static final String REQUESTED_ATTRIBUTE_LOCAL_NAME = "RequestedAttribute";
+	private static final String ATTRIBUTE_NAME = "Name";
+	static final String DISPLAY_INFORMATION_LOCAL_NAME = "DisplayInformation";
+	static final String ORGANIZATION_DISPLAY_NAME_LOCAL_NAME = "OrganizationDisplayName";
+
+	public Extensions createAkdbExtension() {
+		var extensionsElement = new ExtensionsBuilder().buildObject();
+
+		extensionsElement.getUnknownXMLObjects().add(createAuthRequestElement());
+
+		return extensionsElement;
+	}
+
+	XSAny createAuthRequestElement() {
+		var authRequestElement = new XSAnyBuilder().buildObject(NAMESPACE_URI, AUTHENTICATION_REQUEST_LOCAL_NAME, NAMESPACE_PREFIX);
+
+		authRequestElement.getUnknownAttributes().put(new QName(VERSION_LOCAL_PART), VERSION_VALUE);
+		authRequestElement.getUnknownXMLObjects().addAll(List.of(createAuthnMethods(), createRequestedAttributes(), createDisplayInformation()));
+
+		return authRequestElement;
+	}
+
+	XSAny createAuthnMethods() {
+		var allowedMethodsElement = new XSAnyBuilder().buildObject(NAMESPACE_URI, AUTHN_METHODS_LOCAL_NAME, NAMESPACE_PREFIX);
+		var authnMethodMap = properties.getAuthnMethods();
+
+		authnMethodMap.forEach((key, value) -> allowedMethodsElement.getUnknownXMLObjects().add(createMethod(key, value)));
+
+		return allowedMethodsElement;
+	}
+
+	XSAny createMethod(String name, String enabledValue) {
+		var method = new XSAnyBuilder().buildObject(NAMESPACE_URI, name, NAMESPACE_PREFIX);
+		var enabled = new XSAnyBuilder().buildObject(NAMESPACE_URI, ENABLED_LOCAL_NAME, NAMESPACE_PREFIX);
+		enabled.setTextContent(enabledValue);
+
+		method.getUnknownXMLObjects().add(enabled);
+
+		return method;
+	}
+
+	XSAny createRequestedAttributes() {
+		var requestedAttributesElement = new XSAnyBuilder().buildObject(NAMESPACE_URI, REQUESTED_ATTRIBUTES_LOCAL_NAME, NAMESPACE_PREFIX);
+
+		var attributes = properties.getRequestedAttributeUrns();
+		List<XSAny> attributeElements = attributes.stream().map(urn -> {
+			var element = new XSAnyBuilder().buildObject(NAMESPACE_URI, REQUESTED_ATTRIBUTE_LOCAL_NAME, NAMESPACE_PREFIX);
+			element.getUnknownAttributes().put(new QName(ATTRIBUTE_NAME), urn);
+			return element;
+		}).toList();
+
+		requestedAttributesElement.getUnknownXMLObjects().addAll(attributeElements);
+
+		return requestedAttributesElement;
+	}
+
+	XSAny createDisplayInformation() {
+		var displayInformationElement = new XSAnyBuilder().buildObject(NAMESPACE_URI, DISPLAY_INFORMATION_LOCAL_NAME, NAMESPACE_PREFIX);
+		var version = new XSAnyBuilder().buildObject(CLASSIC_UI_URI, VERSION_LOCAL_PART, CLASSIC_UI_PREFIX);
+		var organizationDisplayName = new XSAnyBuilder().buildObject(CLASSIC_UI_URI, ORGANIZATION_DISPLAY_NAME_LOCAL_NAME, CLASSIC_UI_PREFIX);
+		organizationDisplayName.setTextContent(properties.getOrganizationDisplayName());
+
+		version.getUnknownXMLObjects().add(organizationDisplayName);
+		displayInformationElement.getUnknownXMLObjects().add(version);
+
+		return displayInformationElement;
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/DefaultRole.java b/server/src/main/java/de/ozgcloud/antragsraum/security/DefaultRole.java
new file mode 100644
index 0000000000000000000000000000000000000000..a964dcd4d9fc16e6852e0c13625dbff92fd40497
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/DefaultRole.java
@@ -0,0 +1,32 @@
+/*
+ * 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.antragsraum.security;
+
+import org.springframework.security.core.GrantedAuthority;
+
+public class DefaultRole implements GrantedAuthority {
+    public static final String ROLE = "ROLE_USER";
+
+    @Override
+    public String getAuthority() {
+        return ROLE;
+    }
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/InMemoryUserDetailService.java b/server/src/main/java/de/ozgcloud/antragsraum/security/InMemoryUserDetailService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0fe8ede9d10719fc48842495a3043e7db414623
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/InMemoryUserDetailService.java
@@ -0,0 +1,137 @@
+/*
+ * 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.antragsraum.security;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import de.ozgcloud.antragsraum.common.NotFoundException;
+import lombok.NoArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.stereotype.Service;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+@Service
+@NoArgsConstructor
+@Log4j2
+public class InMemoryUserDetailService implements UserDetailsService {
+	@Value("${ozgcloud.code.expire.seconds:10}")
+	private long codeExpireSeconds;
+	@Value("${ozgcloud.jwt.expiration.minutes}")
+	private Long jwtExpirationMinutes;
+	private final ConcurrentHashMap<String, User> usersMap = new ConcurrentHashMap<>();
+
+	Cache<String, String> tmpTokenUserIdCache;
+
+	private Cache<String, String> getTempIdCache() {
+		if (Objects.isNull(tmpTokenUserIdCache)) {
+			tmpTokenUserIdCache = CacheBuilder.newBuilder()
+					.expireAfterAccess(codeExpireSeconds, TimeUnit.SECONDS)
+					.build();
+		}
+
+		return tmpTokenUserIdCache;
+	}
+
+	public void addUser(final String tmpId, final User user) {
+		var thisUser = user;
+		getTempIdCache().put(tmpId, thisUser.getId());
+		thisUser = user.toBuilder().tokenExpiresAt(createExpirationDateTime()).build();
+
+		usersMap.put(thisUser.getId(), thisUser);
+	}
+
+	void setUser(final String tmpId, final User user) {
+		getTempIdCache().put(tmpId, user.getId());
+
+		usersMap.put(user.getId(), user);
+	}
+
+	User getUserByCode(String tmpId) {
+		String userId = getTempIdCache().asMap().get(tmpId);
+
+		if (StringUtils.isEmpty(userId)) {
+			LOG.error("No SAML user linked to code {} found.", tmpId);
+			throw new NotFoundException(User.class, tmpId);
+		}
+		return getUser(userId);
+	}
+
+	User getUser(String id) {
+		return usersMap.get(id);
+	}
+
+	String getSamlTokenOfUser(String userId) {
+		return usersMap.get(userId).getSamlToken();
+	}
+
+	@Override
+	public UserDetails loadUserByUsername(String username) {
+		var optionalEntry = usersMap.entrySet().stream().filter(entry -> username.equals(entry.getValue().getUsername())).findFirst();
+
+		return optionalEntry.map(Map.Entry::getValue).orElseThrow();
+	}
+
+	Optional<User> getUser(AuthCode refreshCode) {
+		return usersMap.entrySet().stream()
+				.filter(entry -> refreshCode.code().equals(entry.getValue().getRefreshCode()))
+				.findFirst()
+				.filter(entry -> entry.getValue().isCredentialsNonExpired())
+				.map(Map.Entry::getValue);
+	}
+
+	public User updateRefreshCodeOf(User user) {
+		var updated = user.toBuilder()
+				.refreshCode(RandomStringUtils.randomAlphanumeric(64))
+				.tokenExpiresAt(createExpirationDateTime()).build();
+		usersMap.put(updated.getId(), updated);
+		return updated;
+	}
+
+	Date createExpirationDateTime() {
+		var nowInstant =ZonedDateTime.now(ZoneId.of("UTC")).plusMinutes(jwtExpirationMinutes).toInstant();
+		LOG.debug("Setting expiry date time to {}", Date.from(nowInstant).toString());
+		return new Date(nowInstant.toEpochMilli());
+	}
+
+	@Scheduled(fixedRate = 4, timeUnit = TimeUnit.HOURS)
+	void userCleanUp() {
+		var expiredEntries = usersMap.entrySet().stream().filter(entry -> !entry.getValue().isCredentialsNonExpired()).toList();
+		expiredEntries.forEach(expiredEntry -> logout(expiredEntry.getValue()));
+	}
+
+	public void logout(User user) {
+		usersMap.remove(user.getId());
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenFilter.java b/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..84fb44283d5a860543c67f598bc4acf53782e07e
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenFilter.java
@@ -0,0 +1,93 @@
+/*
+ * 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.antragsraum.security;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+@Log4j2
+@RequiredArgsConstructor
+@Component
+public class JwtTokenFilter extends OncePerRequestFilter {
+    public static final String TOKEN_HEADER = "Authorization";
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    private final UserDetailsService userDetailsService;
+    private final JwtTokenVerifier jwtTokenVerifier;
+
+    @Override
+    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
+                                    @NonNull FilterChain chain) throws ServletException, IOException {
+        try {
+            getJwtFromRequest(request)
+                .flatMap(jwtTokenVerifier::validate).flatMap(jwsParser -> getJwtFromRequest(request)
+                    .flatMap(token -> jwtTokenVerifier.getJws(jwsParser, token)))
+                .ifPresent(jws -> setUserToSecurityContext(
+                    createAuthentication(request, jws.getPayload().getSubject(), getJwtFromRequest(request))));
+        } catch (NoSuchElementException | IllegalStateException e) {
+            LOG.debug("Cannot set user authentication", e);
+            throw new AccessDeniedException("User not found or invalid token");
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    Authentication createAuthentication(@NonNull HttpServletRequest request, String username,
+                                        Optional<String> tokenOptional) {
+        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
+        String token = tokenOptional.orElse("");
+        UsernamePasswordAuthenticationToken authentication =
+            new UsernamePasswordAuthenticationToken(userDetails, token, userDetails.getAuthorities());
+        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+
+        return authentication;
+    }
+
+    void setUserToSecurityContext(Authentication authentication) {
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+    }
+
+    Optional<String> getJwtFromRequest(HttpServletRequest request) {
+        String tokenHeader = request.getHeader(TOKEN_HEADER);
+        if (StringUtils.hasText(tokenHeader) && tokenHeader.startsWith(TOKEN_PREFIX)) {
+            return Optional.of(tokenHeader.replace(TOKEN_PREFIX, "").trim());
+        }
+        return Optional.empty();
+    }
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenProvider.java b/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6211c04397b9fa43d1f34d70f2044becb45967e
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenProvider.java
@@ -0,0 +1,83 @@
+/*
+ * 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.antragsraum.security;
+
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.stereotype.Component;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import lombok.NonNull;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Component
+public class JwtTokenProvider {
+	public static final String TOKEN_TYPE = "JWT";
+	public static final String TOKEN_ISSUER = "antragsraum-api";
+	public static final String TOKEN_AUDIENCE = "antragsraum-app";
+
+	@Value("${ozgcloud.jwt.secret}")
+	private String jwtSecret;
+
+	public String generate(User user) {
+		List<String> roles = createAuthorities(user);
+
+		byte[] signingKey = jwtSecret.getBytes();
+
+		return createToken(user, signingKey, roles);
+	}
+
+	@NonNull
+	private static List<String> createAuthorities(User user) {
+		return user.getAuthorities()
+		  .stream()
+		  .map(GrantedAuthority::getAuthority)
+		  .toList();
+	}
+
+	private String createToken(User user, byte[] signingKey, List<String> roles) {
+		return Jwts.builder()
+		  .header().add("typ", TOKEN_TYPE)
+		  .and()
+		  .signWith(Keys.hmacShaKeyFor(signingKey), Jwts.SIG.HS512)
+		  .expiration(user.getTokenExpiresAt())
+		  .issuedAt(Date.from(ZonedDateTime.now().toInstant()))
+		  .id(UUID.randomUUID().toString())
+		  .issuer(TOKEN_ISSUER)
+		  .audience().add(TOKEN_AUDIENCE)
+		  .and()
+		  .subject(user.getUsername())
+		  .claim("refreshcode", user.getRefreshCode())
+		  .claim("rol", roles)
+		  .claim("firstname", user.getFirstName())
+		  .claim("lastname", user.getLastName())
+		  .claim("postkorbhandle", user.getPostkorbHandle())
+		  .claim("trustlevel", user.getTrustLevel().getValue())
+		  .compact();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenVerifier.java b/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenVerifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..924081be9c368f3473c32444447fc9bcfddb34b1
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/JwtTokenVerifier.java
@@ -0,0 +1,75 @@
+/*
+ * 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.antragsraum.security;
+
+import java.util.Optional;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.JwtParser;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.UnsupportedJwtException;
+import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+@Component
+public class JwtTokenVerifier {
+	@Value("${ozgcloud.jwt.secret}")
+	private String jwtSecret;
+
+	public Optional<JwtParser> validate(String token) {
+		try {
+			byte[] signingKey = jwtSecret.getBytes();
+
+			var parser = Jwts.parser()
+			  .verifyWith(Keys.hmacShaKeyFor(signingKey))
+			  .build();
+
+			return Optional.of(parser);
+		} catch (ExpiredJwtException exception) {
+			LOG.error("Token {} expired. {}", token, exception.getMessage());
+		} catch (MalformedJwtException exception) {
+			LOG.error("Token {} malformed. {}", token, exception.getMessage());
+		} catch (SignatureException exception) {
+			LOG.error("Token {} signature is invalid. {}", token, exception.getMessage());
+		} catch (IllegalArgumentException | UnsupportedJwtException exception) {
+			LOG.error("Error parsing token {}. Cause: {}", token, exception.getMessage());
+		}
+		return Optional.empty();
+	}
+
+	public Optional<Jws<Claims>> getJws(JwtParser jwtParser, String token) {
+		try {
+			return Optional.of(jwtParser.parseSignedClaims(token));
+		} catch (JwtException | IllegalArgumentException exception) {
+			LOG.error("Error parsing token {}. Cause: {}", token, exception.getMessage());
+		}
+		return Optional.empty();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/SHA256withRSAAndMGF1SignatureAlgorithm.java b/server/src/main/java/de/ozgcloud/antragsraum/security/SHA256withRSAAndMGF1SignatureAlgorithm.java
new file mode 100644
index 0000000000000000000000000000000000000000..096abacff892fa9603afb89fbc7e0cb3f14bcd1d
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/SHA256withRSAAndMGF1SignatureAlgorithm.java
@@ -0,0 +1,61 @@
+/*
+ * 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.antragsraum.security;
+
+import org.opensaml.xmlsec.algorithm.SignatureAlgorithm;
+
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+
+@NoArgsConstructor
+public final class SHA256withRSAAndMGF1SignatureAlgorithm implements SignatureAlgorithm {
+	static final String RSA_ALGORITHM_ID = "RSA";
+	static final String RSA_SHA256_MGF1_ALGORITHM_URL = "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1";
+	static final String RSA_SHA256_MGF1_ALGORITHM_ID = "SHA256withRSAandMGF1";
+	static final String SHA256_ALGORITHM_ID = "SHA-256";
+
+	@NonNull
+	public String getKey() {
+		return RSA_ALGORITHM_ID;
+	}
+
+	@NonNull
+	public String getURI() {
+		return RSA_SHA256_MGF1_ALGORITHM_URL;
+	}
+
+	@NonNull
+	public AlgorithmType getType() {
+		return AlgorithmType.Signature;
+	}
+
+	@NonNull
+	public String getJCAAlgorithmID() {
+		return RSA_SHA256_MGF1_ALGORITHM_ID;
+	}
+
+	@NonNull
+	public String getDigest() {
+		return SHA256_ALGORITHM_ID;
+	}
+}
\ No newline at end of file
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/SamlRedirectStrategy.java b/server/src/main/java/de/ozgcloud/antragsraum/security/SamlRedirectStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..535f6a603825508a30b737e5e7e2a736b2c8bb8d
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/SamlRedirectStrategy.java
@@ -0,0 +1,67 @@
+/*
+ * 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.antragsraum.security;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
+import org.springframework.security.web.DefaultRedirectStrategy;
+
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public class SamlRedirectStrategy extends DefaultRedirectStrategy {
+	private final UserDetailsService userDetailsService;
+	private String onetimeToken;
+
+	public SamlRedirectStrategy(UserDetailsService userDetailsService) {
+		super();
+		this.userDetailsService = userDetailsService;
+	}
+
+	public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url, Authentication authentication) throws IOException {
+		onetimeToken = UUID.randomUUID().toString();
+		if (authentication instanceof Saml2Authentication saml2Authentication) {
+			((InMemoryUserDetailService) userDetailsService).addUser(onetimeToken, UserMapper.map(saml2Authentication));
+		} else {
+			LOG.error("Invalid authentication received. Is {} expected {}", authentication.getClass().getName(), Saml2Authentication.class.getName());
+		}
+
+		sendRedirect(request, response, url);
+	}
+
+	@Override
+	public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
+		String redirectUrl = this.calculateRedirectUrl(request.getContextPath(), url);
+		redirectUrl = redirectUrl + "?code=" + onetimeToken;
+		redirectUrl = response.encodeRedirectURL(redirectUrl);
+
+		LOG.debug("Redirecting to {}", redirectUrl);
+
+		response.sendRedirect(redirectUrl);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/SamlUrlAuthenticationSuccessHandler.java b/server/src/main/java/de/ozgcloud/antragsraum/security/SamlUrlAuthenticationSuccessHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..eca45243927ad3000e7804e69d45bedc31ac4bf9
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/SamlUrlAuthenticationSuccessHandler.java
@@ -0,0 +1,100 @@
+/*
+ * 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.antragsraum.security;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.WebAttributes;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public class SamlUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
+	private final SamlRedirectStrategy redirectStrategy;
+
+	private final Map<String, String> roleTargetUrlMap = new HashMap<>();
+
+	public SamlUrlAuthenticationSuccessHandler(final String redirectUrl, final UserDetailsService userService) {
+		super();
+		redirectStrategy = new SamlRedirectStrategy(userService);
+		roleTargetUrlMap.put(DefaultRole.ROLE, getRedirectUrl(redirectUrl));
+	}
+
+	String getRedirectUrl(String redirectUrl) {
+		return StringUtils.isNoneEmpty(redirectUrl) ? redirectUrl : "/";
+	}
+
+	@Override
+	public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication)
+	  throws IOException {
+		handle(request, response, authentication);
+		clearAuthenticationAttributes(request);
+	}
+
+	protected void handle(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication)
+	  throws IOException {
+		final String targetUrl = determineTargetUrl(authentication);
+
+		if (response.isCommitted()) {
+			LOG.debug("Response has already been committed. Unable to redirect to " + targetUrl);
+			return;
+		}
+
+		redirectStrategy.sendRedirect(request, response, targetUrl, authentication);
+	}
+
+	protected String determineTargetUrl(final Authentication authentication) {
+		final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
+		for (final GrantedAuthority grantedAuthority : authorities) {
+			String authorityName = grantedAuthority.getAuthority();
+			if (roleTargetUrlMap.containsKey(authorityName)) {
+				return roleTargetUrlMap.get(authorityName);
+			}
+		}
+
+		throw new IllegalStateException("Invalid role! User is missing role " + DefaultRole.ROLE);
+	}
+
+	/**
+	 * Removes temporary authentication-related data which may have been stored in the session during the authentication process.
+	 */
+	protected final void clearAuthenticationAttributes(final HttpServletRequest request) {
+		final HttpSession session = request.getSession(false);
+
+		if (session == null) {
+			return;
+		}
+
+		session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/SecurityProvider.java b/server/src/main/java/de/ozgcloud/antragsraum/security/SecurityProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..f48b1d4c11f8d9ba6d52884a08b710c7f0083018
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/SecurityProvider.java
@@ -0,0 +1,48 @@
+/*
+ * 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.antragsraum.security;
+
+import java.security.Security;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+import org.springframework.beans.factory.InitializingBean;
+
+public class SecurityProvider implements InitializingBean {
+	@Override
+	public void afterPropertiesSet() {
+		Security.addProvider(new BouncyCastleProvider());
+
+		registerMukSignatureAlgorithm();
+	}
+
+	private void registerMukSignatureAlgorithm() {
+		var algorithmRegistry = ConfigurationService.get(AlgorithmRegistry.class);
+		if (algorithmRegistry != null) {
+			algorithmRegistry.register(new SHA256withRSAAndMGF1SignatureAlgorithm());
+
+			ConfigurationService.register(AlgorithmRegistry.class, algorithmRegistry);
+		}
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/User.java b/server/src/main/java/de/ozgcloud/antragsraum/security/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac1604191a152acb90aa8437914508b95f1cecb8
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/User.java
@@ -0,0 +1,87 @@
+/*
+ * 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.antragsraum.security;
+
+import lombok.Builder;
+import lombok.Getter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.*;
+
+@Builder(toBuilder = true)
+@Getter
+public class User implements UserDetails {
+	private String id;
+	private String samlToken;
+	private String firstName;
+	private String lastName;
+	private String postkorbHandle;
+	private UserTrustLevel trustLevel;
+	private String username;
+	private String refreshCode;
+	private Date tokenExpiresAt;
+	private String password;
+	private transient List<Map.Entry<String, List<Object>>> unknownAttributes;
+
+	@Override
+	public Collection<? extends GrantedAuthority> getAuthorities() {
+		return List.of(new DefaultRole());
+	}
+
+	@Override
+	public String getPassword() {
+		return password;
+	}
+
+	@Override
+	public boolean isAccountNonExpired() {
+		return true;
+	}
+
+	@Override
+	public boolean isAccountNonLocked() {
+		return true;
+	}
+
+	@Override
+	public boolean isCredentialsNonExpired() {
+		var nowDateTime = Date.from(ZonedDateTime.now(ZoneId.of("UTC")).toInstant());
+		if (Objects.isNull(tokenExpiresAt)) {
+			return false;
+		}
+
+		return tokenExpiresAt.after(nowDateTime);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return true;
+	}
+
+	PasswordEncoder passwordEncoder() {
+		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/UserMapper.java b/server/src/main/java/de/ozgcloud/antragsraum/security/UserMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..312dd2caccbbec390581e640cef865db3bed4797
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/UserMapper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.antragsraum.security;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
+import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+class UserMapper {
+	public static final String POSTKORB_HANDLE_URN = "urn:oid:2.5.4.18";
+	public static final String BK2_URN = "urn:oid:1.3.6.1.4.1.25484.494450.3";
+	public static final String VORNAME_URN = "urn:oid:2.5.4.42";
+	public static final String NACHNAME_URN = "urn:oid:2.5.4.4";
+	public static final String VERTRAUENSNIVEAU_URN = "urn:oid:1.2.40.0.10.2.1.1.261.94";
+	private static final String[] KNOWN_ATTRIBUTES = new String[] { POSTKORB_HANDLE_URN, BK2_URN, VORNAME_URN, NACHNAME_URN, VERTRAUENSNIVEAU_URN };
+
+	static User map(Saml2Authentication authentication) {
+		DefaultSaml2AuthenticatedPrincipal principal = (DefaultSaml2AuthenticatedPrincipal) authentication.getPrincipal();
+		return new User.UserBuilder()
+		  .id(principal.getName())
+		  .username(principal.getName())
+		  .firstName(getFirstName(principal))
+		  .lastName(getLastName(principal))
+		  .postkorbHandle(getPostkornHandle(principal))
+		  .trustLevel(getTrustLevel(principal))
+		  .samlToken(authentication.getSaml2Response())
+		  .refreshCode(RandomStringUtils.randomAlphanumeric(64))
+		  .unknownAttributes(getUnknownAttributes(principal.getAttributes()))
+		  .build();
+	}
+
+	static String getFirstName(DefaultSaml2AuthenticatedPrincipal principal) {
+		return principal.getFirstAttribute(VORNAME_URN);
+	}
+
+	static String getLastName(DefaultSaml2AuthenticatedPrincipal principal) {
+		return principal.getFirstAttribute(NACHNAME_URN);
+	}
+
+	static String getPostkornHandle(DefaultSaml2AuthenticatedPrincipal principal) {
+		return principal.getFirstAttribute(POSTKORB_HANDLE_URN);
+	}
+
+	static UserTrustLevel getTrustLevel(DefaultSaml2AuthenticatedPrincipal principal) {
+		var trustLevel = (String) principal.getFirstAttribute(VERTRAUENSNIVEAU_URN);
+
+		return UserTrustLevel.fromString(trustLevel);
+	}
+
+	static List<Map.Entry<String, List<Object>>> getUnknownAttributes(Map<String, List<Object>> attributes) {
+		return attributes.entrySet().stream().filter(entry -> !ArrayUtils.contains(KNOWN_ATTRIBUTES, entry.getKey())).toList();
+	}
+}
diff --git a/server/src/main/java/de/ozgcloud/antragsraum/security/UserTrustLevel.java b/server/src/main/java/de/ozgcloud/antragsraum/security/UserTrustLevel.java
new file mode 100644
index 0000000000000000000000000000000000000000..369957bb7f759ce774c9fcd5714868abbedca3ad
--- /dev/null
+++ b/server/src/main/java/de/ozgcloud/antragsraum/security/UserTrustLevel.java
@@ -0,0 +1,42 @@
+/*
+ * 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.antragsraum.security;
+
+import java.util.Arrays;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public enum UserTrustLevel {
+	STORK_QAA_LEVEL_1("STORK-QAA-Level-1"),
+	STORK_QAA_LEVEL_3("STORK-QAA-Level-3"),
+	STORK_QAA_LEVEL_4("STORK-QAA-Level-4");
+
+	private final String value;
+
+	public static UserTrustLevel fromString(String value) {
+		return Arrays.stream(UserTrustLevel.values()).filter(trustLevel -> trustLevel.getValue().equals(value)).findFirst().orElse(null);
+	}
+}
diff --git a/ozg-antragsraum-server/src/main/resources/META-INF/LICENSE.txt b/server/src/main/resources/META-INF/LICENSE.txt
similarity index 83%
rename from ozg-antragsraum-server/src/main/resources/META-INF/LICENSE.txt
rename to server/src/main/resources/META-INF/LICENSE.txt
index 889a44ac4ef63561d9e4795f06277a2897e566bf..ca12214fe2b6024a77bc6fd99af5141c0ff0fccf 100644
--- a/ozg-antragsraum-server/src/main/resources/META-INF/LICENSE.txt
+++ b/server/src/main/resources/META-INF/LICENSE.txt
@@ -1,6 +1,5 @@
-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
+Copyright (c) 2023-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 -
diff --git a/server/src/main/resources/application-local.yml b/server/src/main/resources/application-local.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2797463ceca490923fa901face3ca4edd000e74a
--- /dev/null
+++ b/server/src/main/resources/application-local.yml
@@ -0,0 +1,61 @@
+management:
+  server:
+    port: 8080
+
+grpc:
+  client:
+    info-manager:
+      address: static://127.0.0.1:9091
+      negotiation-type: PLAINTEXT
+  ozg_service:
+    stubs:
+      enabled: true
+
+clamav:
+  scanUrl: http://127.0.0.1:3010/api/v1/scan
+  connection:
+    timeoutInS: 10
+  read:
+    timeoutInS: 10
+
+logging:
+  level:
+    "org.springframework.security": WARN
+    "org.springframework.security.access": WARN
+    "org.springframework.http": WARN
+    "org.springframework.web": WARN
+    "de.ozgcloud.antragsraum": INFO
+    "net.devh.boot.grpc": INFO
+    "de.ozgcloud.common.binaryfile": DEBUG
+
+ozgcloud:
+  mock:
+    auth: true
+    password: test
+  grpc:
+    client:
+      negotiation-type: PLAINTEXT
+  jwt:
+    secret: "346593nbdgb8e74t6vw477q34bg83456§$%/&Hgvt78hlsjdgfw8äy.skeiw44tz asjkdefa wlfugwegw"
+  antragsraum:
+    bayernid:
+      redirect-url: "http://localhost:8082"
+spring:
+  security:
+    saml2:
+      relyingparty:
+        registration:
+          bayernid:
+            entity-id: https://antragsraum.ozgcloud.de/
+            signing:
+              credentials:
+                - private-key-location: "classpath:/bayernid-local-sign.key"
+                  certificate-location: "classpath:/bayernid-local-sign.crt"
+            decryption:
+              credentials:
+                - private-key-location: "classpath:/bayernid-local-enc.key"
+                  certificate-location: "classpath:/bayernid-local-enc.crt"
+            assertingparty:
+              singlesignon:
+                sign-request: true
+              metadata-uri: "classpath:/metadata/bayernid-idp-infra.xml"
\ No newline at end of file
diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml
new file mode 100644
index 0000000000000000000000000000000000000000..98a2fb2c6d747298d4b56dd89d1d69ef2a570c33
--- /dev/null
+++ b/server/src/main/resources/application.yml
@@ -0,0 +1,88 @@
+management:
+  server:
+    port: 8081
+  health:
+    livenessState:
+      enabled: true
+    readinessState:
+      enabled: true
+  endpoint:
+    health:
+      group:
+        exploratory:
+          include: livenessState,readinessState,ping
+          show-details: always
+      probes:
+        enabled: true
+    prometheus:
+      enabled: true
+  endpoints:
+    web:
+      exposure:
+        include: health, info, prometheus
+
+ozgcloud:
+  antragsraum:
+    authOrigins:
+        - "http://localhost:8082"
+        - "http://localhost:8080"
+    apiOrigins:
+        - "http://localhost:8082"
+    otherOrigins:
+        - "http://localhost:8082"
+  code:
+    expire:
+      seconds: 30
+  jwt:
+    expiration:
+      minutes: 30
+  upload.max-file-size: 100MB
+  nachricht:
+    send:
+      timeout: 60s
+    poll:
+      interval: 2s
+  bayernid:
+    organizationDisplayName: Antragsraum
+    authnMethods:
+      Authega: "false"
+      Benutzername: "true"
+      eID: "false"
+      eIDAS: "false"
+      Diia: "false"
+      Elster: "true"
+      FINK: "false"
+    requestedAttributeUrns:
+      - "urn:oid:2.5.4.18"
+      - "urn:oid:1.3.6.1.4.1.25484.494450.3"
+      - "urn:oid:1.2.40.0.10.2.1.1.261.94"
+      - "urn:oid:1.3.6.1.4.1.25484.494450.2"
+      - "urn:oid:2.5.4.42"
+      - "urn:oid:2.5.4.4"
+      - "urn:oid:0.9.2342.19200300.100.1.3"
+
+server:
+  forward-headers-strategy: framework
+
+spring:
+  application:
+    name: Antragsraum
+  jackson:
+    deserialization:
+      adjust-dates-to-context-time-zone: false
+  servlet:
+    multipart:
+      max-file-size: 150MB
+      max-request-size: 2GB
+  security:
+    saml2:
+      relyingparty:
+        registration:
+          bayernid:
+            entity-id: https://antragsraum.ozgcloud.de/
+            assertingparty:
+              singlesignon:
+                sign-request: true
+logging:
+  level:
+    "de.ozgcloud": INFO
\ No newline at end of file
diff --git a/server/src/main/resources/log4j2-local.xml b/server/src/main/resources/log4j2-local.xml
new file mode 100644
index 0000000000000000000000000000000000000000..53a089dcbf5ab89df712e71557700838df9e0411
--- /dev/null
+++ b/server/src/main/resources/log4j2-local.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+
+<configuration>
+	<Appenders>
+		<Console name="CONSOLE" target="SYSTEM_OUT">
+			<PatternLayout pattern="[%-5level] %c{1.} %msg%n"/>
+		</Console>
+	</Appenders>
+
+	<Loggers>
+		<Root level="WARN">
+			<appender-ref ref="CONSOLE" />
+		</Root>
+	</Loggers>
+</configuration>
\ No newline at end of file
diff --git a/server/src/main/resources/metadata/bayernid-idp-infra.xml b/server/src/main/resources/metadata/bayernid-idp-infra.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fa127650b89820867683d3895cf84854c9aafd14
--- /dev/null
+++ b/server/src/main/resources/metadata/bayernid-idp-infra.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?><md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+    <md:EntityDescriptor 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:Extensions>
+                <alg:SigningMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" Algorithm="http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1"/>
+            </md:Extensions>
+        </md:IDPSSODescriptor>
+    </md:EntityDescriptor>
+</md:EntitiesDescriptor>
\ No newline at end of file
diff --git a/server/src/main/resources/metadata/bayernid-idp-pre.xml b/server/src/main/resources/metadata/bayernid-idp-pre.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c9cda1524c01c32ab8a7871b4438a1c52e0af2b6
--- /dev/null
+++ b/server/src/main/resources/metadata/bayernid-idp-pre.xml
@@ -0,0 +1,48 @@
+<md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+    <md:EntityDescriptor entityID="https://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://pre-id.bayernportal.de/idp/profile/SAML2/POST/SSO"/>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://pre-id.bayernportal.de/idp/profile/SAML2/Redirect/SSO"/>
+            <md:Extensions>
+                <alg:SigningMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" Algorithm="http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1"/>
+            </md:Extensions>
+        </md:IDPSSODescriptor>
+    </md:EntityDescriptor>
+</md:EntitiesDescriptor>
\ No newline at end of file
diff --git a/server/src/main/resources/metadata/bayernid-idp.xml b/server/src/main/resources/metadata/bayernid-idp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d229429514140fb1f5aa7db1d0d619fc34cbeafa
--- /dev/null
+++ b/server/src/main/resources/metadata/bayernid-idp.xml
@@ -0,0 +1,48 @@
+<md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+    <md:EntityDescriptor entityID="https://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>MIIFbzCCA1egAwIBAgIJAIpEXcxsS1nEMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV
+                            BAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYD
+                            VQQKDARBS0RCMQwwCgYDVQQLDANJRE0wHhcNMjAxMDI3MTMxNzE3WhcNMjUxMDI2
+                            MTMxNzE3WjBOMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQH
+                            DAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwDSURNMIICIjANBgkq
+                            hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4MvmcEZhH4gcNhLLsEBhbB24fbU7ex1K
+                            ZROJr4s8OMqAHryfenCFxxVKPI8Vm2+qg6SY+D+MbKHc3lITYecq0EAQQoV1M1bc
+                            D31qVRtxTVb1Q9qGTv2V72LZB12qCCcgyWT4LOKTnGYUv5dIyj6EkEgVn8+Ia5Ow
+                            Q5KeoCsh9yVo5PJBJXkerQEA/M4/LkT/obeaK+3+8eRGFmoGLb+bFM40ths9St2j
+                            eeYT3UWl9NTY66mq4YVDZcaimUCMJQ4X9SjQ8HZeLgRzLx5X/6PwAJysyKVseN4j
+                            SV+4sm/HnMrPcQLLLQCD0ltdp1jk5zvoZ6C8cZ8FJ7kCJ+Kvz1zEmH32Pw0bxPwB
+                            QsKabyRxg5ESTIWwE2lWOnuooqSWk8lZIaGf8Sn8JFjRaVqVKN6JRFxgryurreJL
+                            9igN1e5cOdKYwXLfuqygcdVW40wYTjsL1MxKVf9Pb8J4EuOC95Hb85X6VckwRCx8
+                            Yroyr/UeDM8SKgsBJuSqTuFCo6oTf3zjDaD+BBzdVqBmQELfY81laG3at1A3vz0M
+                            E9UkY5kv9/LH161YIDELgir63fBNsQSXPir5awP8xihgUlmCIfOXiMU+m9vp60KL
+                            8LzA2AODo2H5AY7BMxAlM+LF+KvPqUBxdq2QyTnhQFaeiP9l7Lk1xagnloiR6rW/
+                            GP+hsBAVO5UCAwEAAaNQME4wHQYDVR0OBBYEFAS91fX286nxHd+4kJSFE4clWiuC
+                            MB8GA1UdIwQYMBaAFAS91fX286nxHd+4kJSFE4clWiuCMAwGA1UdEwQFMAMBAf8w
+                            DQYJKoZIhvcNAQELBQADggIBAMt8FUGWNqtKAuVAklNojt9JXk476XvZNXn4lTjx
+                            IaIyQLd+SpJ4vKPDAb78mKF4PtTdG2pqDIarvuxyLvG4Ut8Cd/NHTI9j9V7Yuw9g
+                            MKPKzXfI9Zb4pOIantmEspRr/BApLPJzZMHI8KmeQ3V0Ip3i8QHMuHfkFuTN9hdS
+                            mKTqoWZTo6dLawOkOpgjKh5RQjqjbXWUovMWQc/tffzUved0LePbPllJgSjUZRxm
+                            +723ToZ1frFCYpFj1xJiOuqr+GJdtWz0DFRt+nXxRfznYtXd1YRC5BzpVwNtH8GL
+                            OZfyH1HZtn1x5hcQOnRHwgcx37lJRW8yprAMiqLHtNYNlY/SD1KNMmqT07pkBwMq
+                            Cgvic7T2yj9Jg38s6XjiwWiFRMNhC2+z9G1uf63tfXLi3J8HS8YyTSjEK+9aXMEj
+                            57HWHBsLAiAzO+RyrVdTEnp0f5qbUeu0d4DKvHocOWkxu0jp220ZlCHPm6f/nWM/
+                            Eo5TGT0htLo0vidoj36PmCu2KOJ+AhdEml81/M5ENg8ofhhkidiQo2he7eGjLyn4
+                            4DPvSAiK/S1rvQmF65j/6mg0guFbGLpmLFQJXPztok2xTA+Vwvd/T5FxMNkKR9mh
+                            P9E8rUGOu4hGB4KSHD8mo8nYyp1cCke72e7dMobhLGsCqxEnIeO+NTBzSbd/gqGm 2fd0
+                        </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://id.bayernportal.de/idp/profile/SAML2/POST/SSO"/>
+            <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://id.bayernportal.de/idp/profile/SAML2/Redirect/SSO"/>
+            <md:Extensions>
+                <alg:SigningMethod xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" Algorithm="http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1"/>
+            </md:Extensions>
+        </md:IDPSSODescriptor>
+    </md:EntityDescriptor>
+</md:EntitiesDescriptor>
\ No newline at end of file
diff --git a/server/src/main/resources/metadata/okta-idp.xml b/server/src/main/resources/metadata/okta-idp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2b294cce4fa05fb4e1b32e86fdd9180ba0728ad9
--- /dev/null
+++ b/server/src/main/resources/metadata/okta-idp.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<md:EntityDescriptor entityID="http://www.okta.com/exkeeichkhdEpMLcK5d7"
+                     xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
+    <md:IDPSSODescriptor WantAuthnRequestsSigned="false"
+                         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>MIIDqDCCApCgAwIBAgIGAYz3208uMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYDVQQGEwJVUzETMBEG
+                        A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
+                        MBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi0wNDg0OTQ4NzEcMBoGCSqGSIb3DQEJ
+                        ARYNaW5mb0Bva3RhLmNvbTAeFw0yNDAxMTEwOTI4NTRaFw0zNDAxMTEwOTI5NTRaMIGUMQswCQYD
+                        VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsG
+                        A1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxFTATBgNVBAMMDGRldi0wNDg0OTQ4NzEc
+                        MBoGCSqGSIb3DQEJARYNaW5mb0Bva3RhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+                        ggEBAMYySzBZX6VmleW0+259bgvQdfruEGwbS1Jg0TKmBTO4gIhBZzyaQY5kq1EkuVrrg3TMnF0G
+                        Gws2LjF99t4L93d4wBX0WrrehEttvPgiz3qMcjlgqbynM1zTw9w/TbO61lFf6kcwaSu6wbTFeMwB
+                        l5WYBRPg/LcUU0Qddw3NKuVXk0BX5iPCUP+nWczmkrpByfYPYFaMUcFxrY0NnMCQ6/VcrZfPN2yu
+                        ZX+fAV2Bx56o4QD73PCB6XXlOLo6p3PlqGpIMf/NDdBB3ACtIbQuc+l7Bm8dvjZOyV665I6jq9ZP
+                        MOX8Qo8SKIZz2Hh9AvHSwIKVW4aKTKxQ0LQ+I78Xr9UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+                        Y+A+hCMIfjICOzdu6cD8AOrm2PIo1zcMR+mmUKt1l+Gw8gku//uIRc55N+Gga6/rYl6Og2fe+uip
+                        qzQMjXTtmizI+unukVZeJDkfy8sBwNvuikHs2no0HpS7uqgb1gvVKhvyelmD6UsW0mef0b+QRJJU
+                        rNn3LBGYYw4sB3aKTQlmhPE2M7dKbV3N6dxjPneZf4lNSEhsXFBnAEcxczqy37SFCj5JcDInmwwF
+                        2gC0dVqEL7S+JFy6xAyDfn4iBoO+DqgMS6OOAN6kvvR5PB+RYnTqT1lWfcqkeHR9fMfpEGPlxgJ7
+                        MrgVs3cAR5kJKpSbdNyoBWswRQlm+jX2wHJK0Q==
+                    </ds:X509Certificate>
+                </ds:X509Data>
+            </ds:KeyInfo>
+        </md:KeyDescriptor>
+        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+                                Location="https://dev-04849487.okta.com/app/dev-04849487_antragsraumsaml_1/exkeeichkhdEpMLcK5d7/slo/saml"/>
+        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+                                Location="https://dev-04849487.okta.com/app/dev-04849487_antragsraumsaml_1/exkeeichkhdEpMLcK5d7/slo/saml"/>
+        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
+        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
+        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+                                Location="https://dev-04849487.okta.com/app/dev-04849487_antragsraumsaml_1/exkeeichkhdEpMLcK5d7/sso/saml"/>
+        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+                                Location="https://dev-04849487.okta.com/app/dev-04849487_antragsraumsaml_1/exkeeichkhdEpMLcK5d7/sso/saml"/>
+    </md:IDPSSODescriptor>
+</md:EntityDescriptor>
\ No newline at end of file
diff --git a/server/src/test/helm-linter-values.yaml b/server/src/test/helm-linter-values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..271aedb1bb82e45d92e0bfe7b032181ab80e8582
--- /dev/null
+++ b/server/src/test/helm-linter-values.yaml
@@ -0,0 +1,57 @@
+#
+# 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.
+#
+imagePullSecret: test-image-secret
+
+ozgcloud:
+  environment: dev
+
+baseUrl: https://test
+
+networkPolicy:
+  dnsServerNamespace: test-dns
+
+grpcclient:
+  infomanager:
+    address: "static://info-manager:9090"
+
+clamav:
+  scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+
+samlRelyingpartyRegistrationBayernid:
+  entityid: "https://test.antragsraum.ozgcloud.de/"
+  signing:
+    privatekey: "classpath:bayernid-test-sign.key"
+    certificate: "classpath:bayernid-test-sign.crt"
+  decryption:
+    privatekey: "classpath:bayernid-test-enc.key"
+    certificate: "classpath:bayernid-test-enc.crt"
+  metadatauri: "classpath:/metadata/bayernid-idp-infra.xml"
+grpcclient:
+  infomanager:
+    address: "static://info-manager:9090"
+    negotiationtype: PLAINTEXT
+antragsraum:
+  logoutSuccessUrl: https://test.antragsraum.de/?logout
+
+samlRegistrationSecretName: bayernid-saml-registration-secret
\ No newline at end of file
diff --git a/server/src/test/helm/create_jwt_secret_network_policy_test.yaml b/server/src/test/helm/create_jwt_secret_network_policy_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..552134a889ff47a8ed8655857298c44f0e99d0d4
--- /dev/null
+++ b/server/src/test/helm/create_jwt_secret_network_policy_test.yaml
@@ -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.
+#
+
+suite: network policy test
+release:
+  name: network-policy-create-antragsraum-jwt-secret
+  namespace: by-helm-test
+templates:
+  - templates/create_jwt_secret_network_policy.yaml
+tests:
+  - it: should match apiVersion
+    asserts:
+      - isAPIVersion:
+          of: networking.k8s.io/v1
+  - it: should match kind
+    asserts:
+      - isKind:
+          of: NetworkPolicy
+  - it: validate metadata
+    asserts:
+      - equal:
+          path: metadata
+          value:
+            name: network-policy-create-antragsraum-jwt-secret
+            namespace: by-helm-test
+  - it: validate spec
+    asserts:
+      - equal:
+          path: spec
+          value:
+            podSelector:
+              matchLabels:
+                component: create-antragsraum-jwt-secret
+            policyTypes:
+            - Egress
+            egress:
+            # kube api
+            - to:
+              - ipBlock: 
+                  cidr: 172.18.0.1/32
+              - ipBlock:
+                  cidr: 10.0.0.0/8
+              ports:
+              - port: 443
+              - port: 6443
+
+  - it: test network policy disabled
+    set:
+      networkPolicy:
+        disabled: true
+    asserts:
+      - hasDocuments:
+          count: 0
+
+  - it: test network policy unset should be disabled
+    set:
+      networkPolicy:
+        disabled: false
+    asserts:
+      - hasDocuments:
+          count: 1
\ No newline at end of file
diff --git a/server/src/test/helm/create_jwt_secret_rbac_test.yaml b/server/src/test/helm/create_jwt_secret_rbac_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ed973f899b8da1c838bc8e6ddc697f442a693ff6
--- /dev/null
+++ b/server/src/test/helm/create_jwt_secret_rbac_test.yaml
@@ -0,0 +1,94 @@
+#
+# 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.
+#
+
+suite: Create jwt secret rbac
+release:
+  namespace: by-helm-test
+templates:
+  - templates/create_jwt_secret_rbac.yaml
+tests:
+  - it: validate ServiceAccount
+    asserts:
+      - isKind:
+          of: ServiceAccount
+        documentIndex: 0
+      - equal:
+          path: metadata.name
+          value: create-antragsraum-jwt-secret-service-account
+        documentIndex: 0
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+        documentIndex: 0
+  - it: validate RoleBinding
+    asserts:
+      - isKind:
+          of: RoleBinding
+        documentIndex: 1
+      - equal:
+          path: metadata.name
+          value: create-antragsraum-jwt-secret-role-binding
+        documentIndex: 1
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+        documentIndex: 1
+      - contains:
+          path: subjects
+          content:
+            kind: ServiceAccount
+            name: create-antragsraum-jwt-secret-service-account
+        documentIndex: 1
+      - equal:
+          path: roleRef.kind
+          value: Role
+        documentIndex: 1
+      - equal:
+          path: roleRef.name
+          value: create-antragsraum-jwt-secret-role
+        documentIndex: 1
+      - equal:
+          path: roleRef.apiGroup
+          value: rbac.authorization.k8s.io
+        documentIndex: 1
+  - it: validate Role
+    asserts:
+      - isKind:
+          of: Role
+        documentIndex: 2
+      - equal:
+          path: metadata.name
+          value: create-antragsraum-jwt-secret-role
+        documentIndex: 2
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+        documentIndex: 2
+      - contains:
+          path: rules
+          content: 
+            apiGroups: [""]
+            resources: ["secrets"]
+            verbs: ["get", "list", "create"]
+        documentIndex: 2
\ No newline at end of file
diff --git a/server/src/test/helm/create_jwt_secret_script_configmap_test.yaml b/server/src/test/helm/create_jwt_secret_script_configmap_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1cbfe200049ea627f3586cbae244c1ab1e37e5a1
--- /dev/null
+++ b/server/src/test/helm/create_jwt_secret_script_configmap_test.yaml
@@ -0,0 +1,73 @@
+#
+# 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.
+#
+
+suite: Create jwt secret script ConfigMap
+release:
+  namespace: by-helm-test
+templates:
+  - templates/create_jwt_secret_script_configmap.yaml
+tests:
+  - it: validate configMap values
+    asserts:
+      - isKind:
+          of: ConfigMap
+      - equal:
+          path: metadata.name
+          value: create-antragsraum-jwt-secret-script
+      - equal:
+          path: metadata.namespace
+          value: by-helm-test
+  - it: validate default script data
+    asserts:
+      - equal:
+          path: data
+          value:
+             create-antragsraum-jwt-secret.sh: |
+                #!/bin/sh
+
+                kubectl get secrets -n by-helm-test -o json >> /tmp/secrets.json
+
+                jq '.items[] | .metadata.name' /tmp/secrets.json
+
+                secretName="antragsraum-jwt-secret"
+
+                antragsraumJWTSecretExists=$(jq -e ".items[] | select(.metadata.name == \"$secretName\")" /tmp/secrets.json > /dev/null ; echo "$?")
+
+                echo "$secretName status: $antragsraumJWTSecretExists"
+
+                if [ "$antragsraumJWTSecretExists" -ne 0 ]
+                then
+                  echo "Erstelle neues Secret: $secretName"
+                  kubectl create secret generic $secretName --namespace=by-helm-test --from-literal="jwt-secret=$(openssl rand -base64 72)"
+                else
+                  echo "Kein neues Secret für antragsraum jwt"
+                fi
+
+ 
+ 
+ 
+ 
+ 
+ 
+ 
diff --git a/server/src/test/helm/create_secret_pod_test.yaml b/server/src/test/helm/create_secret_pod_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1c83a7c2cd5f2cf15a4e350ecc72315c7ed8f2a1
--- /dev/null
+++ b/server/src/test/helm/create_secret_pod_test.yaml
@@ -0,0 +1,98 @@
+#
+# 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.
+#
+
+suite: test create jwt secret pod
+release:
+  name: ozg-database
+  namespace: by-helm-test
+templates:
+  - templates/create_secret_pod.yaml
+tests:
+  - it: validate pod
+    asserts:
+      - isKind:
+          of: Pod
+      - equal:
+          path: metadata.name
+          value: "create-antragsraum-jwt-secret"
+      - equal:
+          path: metadata.namespace
+          value: "by-helm-test"
+      - equal:
+          path: metadata.labels.component
+          value: create-antragsraum-jwt-secret
+      - equal:
+          path: spec.serviceAccountName
+          value: "create-antragsraum-jwt-secret-service-account"
+      - equal:
+          path: spec.restartPolicy
+          value: "Never"
+      - equal:
+          path: spec.containers[0].name
+          value: create-antragsraum-jwt-secret
+      - equal:
+          path: spec.containers[0].image
+          value: dockerproxy.ozg-sh.de/bitnami/kubectl:latest
+      - equal:
+          path: spec.containers[0].command
+          value: ["/bin/sh", "-c", "./script/create-antragsraum-jwt-secret.sh"]
+      - contains:
+          path: spec.containers[0].volumeMounts
+          content:
+            mountPath: "/script"
+            name: "create-antragsraum-jwt-secret-volume"
+      - contains:
+          path: spec.volumes
+          content:
+            name: "create-antragsraum-jwt-secret-volume"
+            configMap:
+              defaultMode: 493
+              name: create-antragsraum-jwt-secret-script
+      - notExists:
+          path: spec.imagePullSecrets
+
+  - it: validate set kubectl image
+    templates:
+      - templates/create_secret_pod.yaml
+    set:
+      image:
+        kubectl:
+          repo: kubeclt-repo
+          name: kubeclt
+          tag: test
+    asserts:
+      - equal:
+          path: spec.containers[0].image
+          value: kubeclt-repo/kubeclt:test
+
+  - it: validate set imagePull secret
+    templates:
+      - templates/create_secret_pod.yaml
+    set:
+      imagePullSecret: test-image-pull-secret
+    asserts:
+      - contains:
+          path: spec.imagePullSecrets
+          content:
+            name: test-image-pull-secret
diff --git a/server/src/test/helm/deployment_63_char_test.yaml b/server/src/test/helm/deployment_63_char_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1ca8124ff7a14d674f34bda97e56c0541645502c
--- /dev/null
+++ b/server/src/test/helm/deployment_63_char_test.yaml
@@ -0,0 +1,64 @@
+#
+# 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.
+#
+
+suite: test deyploment less than 63 chars
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: test
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+  - it: should fail on .Release.Namespace length longer than 63 characters
+    release:
+      namespace: test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890
+    asserts:
+      - failedTemplate:
+          errorMessage: .Release.Namespace test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 ist zu lang (max. 63 Zeichen)
+  - it: should not fail on .Release.Namespace length less than 63 characters
+    asserts:
+      - notFailedTemplate: {}
+  - it: should fail on .Chart.Name-.Chart.Version length longer than 63 characters
+    chart:
+      version: 1.0-test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890
+    asserts:
+      - failedTemplate:
+          errorMessage: .Chart.Name-.Chart.Version antragsraum-server-1.0-test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 ist zu lang (max. 63 Zeichen)
+  - it: should not fail on .Chart.Name-.Chart.Version length less than 63 characters
+    asserts:
+      - notFailedTemplate: {}
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_actuator_test.yaml b/server/src/test/helm/deployment_actuator_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d5ab71b1da8d359d680f3a081e7499c2798e3f47
--- /dev/null
+++ b/server/src/test/helm/deployment_actuator_test.yaml
@@ -0,0 +1,75 @@
+#
+# 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.
+#
+
+suite: test deployment actuator 
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: testIsDeployment
+    template: deployment.yaml
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].readinessProbe
+          value:
+            failureThreshold: 3
+            httpGet:
+              path: /actuator/health/readiness
+              port: 8081
+              scheme: HTTP
+            periodSeconds: 10
+            initialDelaySeconds: 120
+            successThreshold: 1
+            timeoutSeconds: 3
+      - equal: 
+          path: spec.template.spec.containers[0].startupProbe
+          value: 
+            httpGet:
+              path: /actuator/health/readiness
+              port: 8081
+              scheme: HTTP
+            failureThreshold: 10
+            initialDelaySeconds: 120
+            periodSeconds: 10
+            successThreshold: 1
+            timeoutSeconds: 5    
+        
+
+
+
diff --git a/server/src/test/helm/deployment_bindings_test.yaml b/server/src/test/helm/deployment_bindings_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..bb290ce20f1a473a95d783b53fa0f6f1132dc924
--- /dev/null
+++ b/server/src/test/helm/deployment_bindings_test.yaml
@@ -0,0 +1,107 @@
+#
+# 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.
+#
+
+suite: deployment antragsraum-server bindings
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+  - it: should have volumeMounts for temp folder
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].volumeMounts
+          content:
+            name: tmp-volume
+            mountPath: "/tmp/antragsraum"
+            
+  - it: should have volume for temp folder
+    asserts:
+      - contains:
+           path: spec.template.spec.volumes
+           content:
+              name: tmp-volume
+              emptyDir: {}
+  - it: should have volume for saml secrets
+    asserts:
+      - contains:
+          path: spec.template.spec.volumes
+          content:
+            name: saml-mount
+            secret:
+              secretName: bayernid-saml-registration-secret
+              optional: false
+            
+  - it: should have volumeMounts for saml secrets
+    asserts:
+      - contains:
+           path: spec.template.spec.containers[0].volumeMounts
+           content:
+              name: saml-mount
+              mountPath: "/keystore/enc.crt"
+              subPath: enc.crt
+              readOnly: true
+      - contains:
+           path: spec.template.spec.containers[0].volumeMounts
+           content:
+              name: saml-mount
+              mountPath: "/keystore/enc.key"
+              subPath: enc.key
+              readOnly: true
+      - contains:
+           path: spec.template.spec.containers[0].volumeMounts
+           content:
+              name: saml-mount
+              mountPath: "/keystore/signing.crt"
+              subPath: signing.crt
+              readOnly: true
+      - contains:
+           path: spec.template.spec.containers[0].volumeMounts
+           content:
+              name: saml-mount
+              mountPath: "/keystore/signing.key"
+              subPath: signing.key
+              readOnly: true
+      - contains:
+           path: spec.template.spec.containers[0].volumeMounts
+           content:
+              name: saml-mount
+              mountPath: "/metadata/bayernid-idp-infra.xml"
+              subPath: metadata.xml
+              readOnly: true
diff --git a/server/src/test/helm/deployment_container_basic_test.yaml b/server/src/test/helm/deployment_container_basic_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..72ab81318b8a20d9500dc5b046a3ff8369f0f59a
--- /dev/null
+++ b/server/src/test/helm/deployment_container_basic_test.yaml
@@ -0,0 +1,72 @@
+#
+# 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.
+#
+
+suite: test deployment container basics
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+  - it: should have correct values for container image, name, imagePullPolicy
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].image
+          value: docker.ozg-sh.de/antragsraum-server:latest
+      - equal:
+          path: spec.template.spec.containers[0].name
+          value: antragsraum-server
+      - equal: 
+          path: spec.template.spec.containers[0].imagePullPolicy
+          value: Always
+  - it: should have correct values for container ports
+    asserts:
+      - contains: 
+          path: spec.template.spec.containers[0].ports
+          content:  
+              containerPort: 8080
+              name: 8080tcp1
+              protocol: TCP
+      - contains: 
+          path: spec.template.spec.containers[0].ports
+          content:  
+              containerPort: 8081
+              name: metrics
+              protocol: TCP          
+        
+        
diff --git a/server/src/test/helm/deployment_container_other_values_test.yaml b/server/src/test/helm/deployment_container_other_values_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0a0cfb395df8c409c35d79ee9bdb00abc5901fe5
--- /dev/null
+++ b/server/src/test/helm/deployment_container_other_values_test.yaml
@@ -0,0 +1,59 @@
+   
+   #
+# 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.
+#
+
+suite: test deployment container other values
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: should have correct values for container terminationMessagePolicy, terminationMessagePath, stdin, tty
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].terminationMessagePolicy
+          value: File
+      - equal:
+          path: spec.template.spec.containers[0].terminationMessagePath
+          value: /dev/termination-log
+      - equal: 
+          path: spec.template.spec.containers[0].stdin
+          value: true
+      - equal: 
+          path: spec.template.spec.containers[0].tty
+          value: true
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_container_security_context_test.yaml b/server/src/test/helm/deployment_container_security_context_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..2d990e4ef5808801b3a3c672f0a0b3153f8b659b
--- /dev/null
+++ b/server/src/test/helm/deployment_container_security_context_test.yaml
@@ -0,0 +1,77 @@
+#
+# 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.
+#
+
+suite: test deployment container security context
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+  - it: check default values of spec.containers[0].securityContext
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation
+          value: false
+      - equal:
+          path: spec.template.spec.containers[0].securityContext.privileged
+          value: false
+      - equal:
+          path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem
+          value: false
+      - equal:
+          path: spec.template.spec.containers[0].securityContext.runAsNonRoot
+          value: true
+      - isNull:
+          path: spec.template.spec.containers[0].securityContext.runAsUser
+      - isNull:
+          path: spec.template.spec.containers[0].securityContext.runAsGroup
+  - it: check containers runAsUser
+    set:
+      securityContext.runAsUser: 1000
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].securityContext.runAsUser
+          value: 1000
+  - it: check runAsGroup
+    set:
+      securityContext.runAsGroup: 1000
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].securityContext.runAsGroup
+          value: 1000
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_defaults_labels_test.yaml b/server/src/test/helm/deployment_defaults_labels_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1399f4173a9582628d4043ccb275d566933ca5f0
--- /dev/null
+++ b/server/src/test/helm/deployment_defaults_labels_test.yaml
@@ -0,0 +1,79 @@
+#
+# 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.
+#
+
+suite: test deployment default labels
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: check metadata.labels
+    asserts:
+      - equal:
+          path: metadata.labels
+          value:
+            app.kubernetes.io/instance: antragsraum-server
+            app.kubernetes.io/managed-by: Helm
+            app.kubernetes.io/name: antragsraum-server
+            app.kubernetes.io/namespace: sh-helm-test
+            app.kubernetes.io/part-of: ozgcloud
+            app.kubernetes.io/version: 0.0.0-MANAGED-BY-JENKINS
+            helm.sh/chart: antragsraum-server-0.0.0-MANAGED-BY-JENKINS      
+  
+  - it: should set spec.selector.matchLabels
+    asserts:
+      - equal:
+          path: spec.selector.matchLabels
+          value:
+            app.kubernetes.io/name: antragsraum-server
+            app.kubernetes.io/namespace: sh-helm-test
+
+
+  - it: should have correct deyploment spec.template.metadata.labels
+    asserts:
+      - equal:
+          path: spec.template.metadata.labels
+          value: 
+            app.kubernetes.io/instance: antragsraum-server
+            app.kubernetes.io/managed-by: Helm
+            app.kubernetes.io/name: antragsraum-server
+            app.kubernetes.io/namespace: sh-helm-test
+            app.kubernetes.io/part-of: ozgcloud
+            app.kubernetes.io/version: 0.0.0-MANAGED-BY-JENKINS
+            component: antragsraum-server
+            helm.sh/chart: antragsraum-server-0.0.0-MANAGED-BY-JENKINS
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_defaults_topologySpreadConstraints_test.yaml b/server/src/test/helm/deployment_defaults_topologySpreadConstraints_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1e0c48ecd34d9ed8f987d2678ea1999b237679d3
--- /dev/null
+++ b/server/src/test/helm/deployment_defaults_topologySpreadConstraints_test.yaml
@@ -0,0 +1,60 @@
+#
+# 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.
+#
+
+suite: test deployment topology spread constrains
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:  
+  ozgcloud:
+    environment: test
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: check default values of spec.template.spec.topologySpreadConstraints
+    asserts:
+      - isKind:
+          of: Deployment
+      - equal:
+          path: spec.template.spec.topologySpreadConstraints[0].maxSkew
+          value: 1
+      - equal:
+          path: spec.template.spec.topologySpreadConstraints[0].topologyKey
+          value: kubernetes.io/hostname
+      - equal:
+          path: spec.template.spec.topologySpreadConstraints[0].whenUnsatisfiable
+          value: ScheduleAnyway
+      - equal:
+          path: spec.template.spec.topologySpreadConstraints[0].labelSelector.matchLabels["app.kubernetes.io/name"]
+          value: antragsraum-server
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_env_test.yaml b/server/src/test/helm/deployment_env_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..885a21a5d33e572fec89a628c9c5e1a9af19b1f9
--- /dev/null
+++ b/server/src/test/helm/deployment_env_test.yaml
@@ -0,0 +1,169 @@
+#
+# 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.
+#
+
+suite: test deployment container environments 
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: check customList as list
+    set:
+      env.customList:
+        - name: my_test_environment_name
+          value: "A test value"
+        - name: test_environment
+          value: "B test value"
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: my_test_environment_name
+            value: "A test value"
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: test_environment
+            value: "B test value"
+  - it: check customList as dict
+    set:
+      env.customList:
+        my_test_environment_name: "A test value"
+        test_environment: "B test value"
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: my_test_environment_name
+            value: "A test value"
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: test_environment
+            value: "B test value"
+
+  - it: check customList test value is not set by default
+    asserts:
+      - notContains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: my_test_environment_name
+            value: "A test value"
+
+
+  - it: should contain GRPC_CLIENT_INFO-MANAGER_ADDRESS as env
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+             name: GRPC_CLIENT_INFO-MANAGER_ADDRESS
+             value: static://info-manager:9090
+
+  - it: should contain clamav scan url as env
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: CLAMAV_SCANURL
+            value: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+          
+  - it: should have jwt-secret
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: OZGCLOUD_JWT_SECRET
+            valueFrom:
+              secretKeyRef:
+                name: antragsraum-jwt-secret
+                key: jwt-secret
+                optional: false
+
+  - it: should contain saml envs 
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_SIGNING_CREDENTIALS_0_PRIVATE-KEY-LOCATION
+            value: file:///keystore/signing.key
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_SIGNING_CREDENTIALS_0_CERTIFICATE-LOCATION
+            value: file:///keystore/signing.crt
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_DECRYPTION_CREDENTIALS_0_PRIVATE-KEY-LOCATION
+            value: file:///keystore/enc.key
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_DECRYPTION_CREDENTIALS_0_CERTIFICATE-LOCATION
+            value: file:///keystore/enc.crt
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: SPRING_SECURITY_SAML2_RELYINGPARTY_REGISTRATION_BAYERNID_ASSERTINGPARTY_METADATA-URI
+            value: file:///metadata/bayernid-idp-infra.xml
+                                    
+  - it: should have jwt-secret env 
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+            name: OZGCLOUD_JWT_SECRET
+            valueFrom:
+              secretKeyRef:
+                key: jwt-secret
+                name: antragsraum-jwt-secret
+                optional: false
+
+  - it: should have GRPC_CLIENT_INFO-MANAGER_NEGOTIATIONTYPE 
+    asserts:  
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+             name: GRPC_CLIENT_INFO-MANAGER_NEGOTIATIONTYPE
+             value: PLAINTEXT
+            
+
+  - it: should have correcct OZGCLOUD_ANTRAGSRAUM_LOGOUTSUCCESSURL
+    asserts:  
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content: 
+             name: OZGCLOUD_ANTRAGSRAUM_LOGOUTSUCCESSURL
+             value: https://test.antragsraum.de/?logout
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_host_aliases_test.yaml b/server/src/test/helm/deployment_host_aliases_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..63224918186471e099c518e6f08d7e796068c02b
--- /dev/null
+++ b/server/src/test/helm/deployment_host_aliases_test.yaml
@@ -0,0 +1,63 @@
+#
+# 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.
+#
+
+suite: deployment host aliases
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:  
+  ozgcloud:
+    environment: test
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: should not set spec.template.spec.hostAliases
+    asserts:
+      - isNull:
+          path: spec.template.spec.hostAliases
+  - it: should set spec.template.spec.hostAliases
+    set:
+      hostAliases:
+        - ip: "127.0.0.1"
+          hostname:
+          - "eins"
+          - "zwei"
+    asserts:
+      - contains:
+          path: spec.template.spec.hostAliases
+          content:
+            ip: "127.0.0.1"
+            hostname:
+            - "eins"
+            - "zwei"
diff --git a/server/src/test/helm/deployment_imagepull_secret_test.yaml b/server/src/test/helm/deployment_imagepull_secret_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..76156389aeebc334d4a94e5566785fd7650667c7
--- /dev/null
+++ b/server/src/test/helm/deployment_imagepull_secret_test.yaml
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+suite: test deployment image pull secret
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: should use correct imagePull secret
+    asserts:
+      - equal:
+          path: spec.template.spec.imagePullSecrets[0].name
+          value: test-image-secret
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_pod_general_values_test.yaml b/server/src/test/helm/deployment_pod_general_values_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..66ad09c9fc3aba1e9e0b65f9d227624526a8d1e0
--- /dev/null
+++ b/server/src/test/helm/deployment_pod_general_values_test.yaml
@@ -0,0 +1,63 @@
+ #
+# 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.
+#
+
+suite: test deployment container basics
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://dev.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+ - it: should have correct pod template values
+   asserts:
+      - isEmpty:
+          path: spec.template.spec.dnsConfig
+      - equal:
+          path: spec.template.spec.dnsPolicy
+          value: "ClusterFirst"
+      - equal:
+          path: spec.template.spec.restartPolicy
+          value: "Always"
+      - equal:
+          path: spec.template.spec.schedulerName
+          value: "default-scheduler"
+      - isEmpty:
+          path: spec.template.spec.securityContext
+      - equal:
+          path: spec.template.spec.terminationGracePeriodSeconds
+          value: 30
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_resources_test.yaml b/server/src/test/helm/deployment_resources_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3ac201e84de69645902fe1c1b78fdf38ae9debcb
--- /dev/null
+++ b/server/src/test/helm/deployment_resources_test.yaml
@@ -0,0 +1,71 @@
+#
+# 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.
+#
+
+suite: test deployment container resources
+release:
+  name: antragsraum-server
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+  - it: should generate resources when values set
+    set:
+      resources:
+        limits:
+          cpu: 11m
+          memory: 22Mi
+        requests:
+          cpu: 33m
+          memory: 44Mi
+    asserts:
+      - equal:
+          path: spec.template.spec.containers[0].resources.limits.cpu
+          value: 11m
+      - equal:
+          path: spec.template.spec.containers[0].resources.limits.memory
+          value: 22Mi
+      - equal:
+          path: spec.template.spec.containers[0].resources.requests.cpu
+          value: 33m
+      - equal:
+          path: spec.template.spec.containers[0].resources.requests.memory
+          value: 44Mi
+  - it: should not generate resources when values not set
+    asserts:
+      - isEmpty:
+          path: spec.template.spec.containers[0].resources
+
diff --git a/server/src/test/helm/deployment_service_account_test.yaml b/server/src/test/helm/deployment_service_account_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..55119cc010b3a9b10cfb37aa92417060fafd96fc
--- /dev/null
+++ b/server/src/test/helm/deployment_service_account_test.yaml
@@ -0,0 +1,65 @@
+#
+# 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.
+#
+
+suite: deployment service account
+release:
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+  - it: should use service account with default name
+    set:
+      serviceAccount:
+        create: true
+    asserts:
+      - equal:
+          path: spec.template.spec.serviceAccountName
+          value: antragsraum-server-service-account
+  - it: should use service account with name
+    set:
+      serviceAccount:
+        create: true
+        name: helm-service-account
+    asserts:
+      - equal:
+          path: spec.template.spec.serviceAccountName
+          value: helm-service-account
+  - it: should use default service account
+    asserts:
+      - isNull:
+          path: spec.template.spec.serviceAccountName
\ No newline at end of file
diff --git a/server/src/test/helm/deployment_springProfile_test.yaml b/server/src/test/helm/deployment_springProfile_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8097c1ce985542e89a98f19657b005c06264be4e
--- /dev/null
+++ b/server/src/test/helm/deployment_springProfile_test.yaml
@@ -0,0 +1,60 @@
+#
+# 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.
+#
+
+suite: test deployment spring profiles
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:  
+  ozgcloud:
+    environment: test
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+tests:
+  - it: should override the spring profiles
+    set:
+      env.overrideSpringProfiles: oc,test,ea
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: spring_profiles_active
+            value: oc,test,ea
+  - it: should generate the spring profiles
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: spring_profiles_active
+            value: oc, test
\ No newline at end of file
diff --git a/server/src/test/helm/deyploment_general_value_test.yaml b/server/src/test/helm/deyploment_general_value_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..794e915fa4a611268ca4fe2def6e85138d1ccfa9
--- /dev/null
+++ b/server/src/test/helm/deyploment_general_value_test.yaml
@@ -0,0 +1,84 @@
+#
+# 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.
+#
+
+suite: test deployment general values
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    environment: dev
+  imagePullSecret: test-image-secret
+  grpcclient:
+    infomanager:
+      address: "static://info-manager:9090"
+      negotiationtype: PLAINTEXT
+  clamav:
+    scanUrl: https://cra-service.ozg-clamav.svc.cluster.local:3000/api/v1/scan
+  antragsraum:
+    logoutSuccessUrl: https://test.antragsraum.de/?logout
+  samlRegistrationSecretName: bayernid-saml-registration-secret
+
+tests:
+  - it: should have correct apiVersion
+    asserts:
+      - isKind:
+          of: Deployment
+      - isAPIVersion:
+           of: "apps/v1"
+      
+  - it: should have correct deployment metadata 
+    asserts: 
+      - equal:
+          path: metadata.name
+          value: antragsraum-server
+      - equal: 
+          path: metadata.namespace
+          value: sh-helm-test
+
+
+  - it: should have correct deyployment general spec values
+    asserts:
+      - equal:
+          path: spec.progressDeadlineSeconds
+          value: 600
+      - equal:
+          path: spec.replicas
+          value: 1
+      - equal:
+          path: spec.revisionHistoryLimit
+          value: 10
+  - it: should have correct deployment spec strategy values
+    asserts:
+      - equal: 
+          path: spec.strategy
+          value: 
+            rollingUpdate:
+              maxSurge: 1
+              maxUnavailable: 0
+            type: RollingUpdate
+          
+
diff --git a/server/src/test/helm/network_policy_test.yaml b/server/src/test/helm/network_policy_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..9c0fe44b06bcbd827089c07ee7cceef81e099545
--- /dev/null
+++ b/server/src/test/helm/network_policy_test.yaml
@@ -0,0 +1,175 @@
+#
+# 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.
+#
+
+suite: network policy antragsraum-server test
+release:
+  name: antragsraum-server
+  namespace: by-helm-test
+templates:
+  - templates/network_policy.yaml
+set:
+  networkPolicy:
+    dnsServerNamespace: kube-system
+tests:
+  - it: should match apiVersion
+    asserts:
+      - isAPIVersion:
+          of: networking.k8s.io/v1
+  - it: should match kind
+    asserts:
+      - isKind:
+          of: NetworkPolicy
+  - it: validate metadata
+    asserts:
+      - equal:
+          path: metadata
+          value:
+            name: network-policy-antragsraum-server
+            namespace: by-helm-test
+  - it: validate spec
+    asserts:
+      - equal:
+          path: spec
+          value: 
+            egress:
+              - ports:
+                  - port: 9090
+                    protocol: TCP
+                to:
+                  - podSelector:
+                      matchLabels:
+                        component: info-manager
+              - ports:
+                  - port: 9090
+                    protocol: TCP
+                to:
+                  - namespaceSelector: {}
+                    podSelector:
+                     matchLabels:
+                       app.kubernetes.io/instance: vorgang-manager
+              - ports:
+                  - port: 53
+                    protocol: UDP
+                  - port: 53
+                    protocol: TCP
+                  - port: 5353
+                    protocol: UDP
+                  - port: 5353
+                    protocol: TCP
+                to:
+                  - namespaceSelector:
+                      matchLabels:
+                        kubernetes.io/metadata.name: kube-system
+            ingress:
+              - ports:
+                  - port: 8080
+            podSelector:
+              matchLabels:
+                component: antragsraum-server
+            policyTypes:
+              - Ingress
+              - Egress
+          
+  - it: add ingress rule by values local
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-namespace-dns
+        additionalIngressConfigLocal:
+          - from:
+            - podSelector: 
+                matchLabels:
+                  component: client2
+    asserts:
+      - contains:
+          path: spec.ingress
+          content:
+            from:
+            - podSelector: 
+                matchLabels:
+                  component: client2
+  - it: add ingress rule by values global
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-namespace-dns
+        additionalIngressConfigGlobal:
+          - from:
+            - podSelector: 
+                matchLabels:
+                  component: client2
+    asserts:
+      - contains:
+          path: spec.ingress
+          content:
+            from:
+            - podSelector: 
+                matchLabels:
+                  component: client2
+
+  - it: add egress rules by values local
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-dns-namespace
+        additionalEgressConfigLocal:
+          - to:
+            - ipBlock:
+                cidr: 1.2.3.4/32
+    asserts:
+      - contains:
+          path: spec.egress
+          content:
+            to:
+            - ipBlock:
+                cidr: 1.2.3.4/32
+  - it: add egress rules by values Global
+    set:
+      networkPolicy:
+        dnsServerNamespace: test-dns-namespace
+        additionalEgressConfigGlobal:
+          - to:
+            - ipBlock:
+                cidr: 1.2.3.4/32
+    asserts:
+      - contains:
+          path: spec.egress
+          content:
+            to:
+            - ipBlock:
+                cidr: 1.2.3.4/32
+
+  - it: test network policy disabled
+    set:
+      networkPolicy:
+        disabled: true
+    asserts:
+      - hasDocuments:
+          count: 0
+
+  - it: test network policy unset should be disabled
+    set:
+      networkPolicy:
+        disabled: false
+        dnsServerNamespace: test-dns-server-namespace
+    asserts:
+      - hasDocuments:
+          count: 1
\ No newline at end of file
diff --git a/server/src/test/helm/service_test.yaml b/server/src/test/helm/service_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8ddfdc87c8682647c7d2e280d286112cb7c08cc6
--- /dev/null
+++ b/server/src/test/helm/service_test.yaml
@@ -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.
+#
+
+suite: test service
+release:
+  name: antragsraum-server
+  namespace: sh-helm-test
+templates:
+  - templates/service.yaml
+tests:
+  - it: should have basic infomation
+    asserts:
+      - isKind:
+          of: Service
+      - isAPIVersion:
+           of: v1
+      - equal: 
+          path: metadata.name
+          value: antragsraum-server
+      - equal: 
+          path: metadata.namespace
+          value: sh-helm-test
+
+  - it: should have the labels with correct value
+    asserts:
+      - equal:
+          path: metadata.labels
+          value: 
+            app.kubernetes.io/instance: antragsraum-server
+            app.kubernetes.io/managed-by: Helm
+            app.kubernetes.io/name: antragsraum-server
+            app.kubernetes.io/namespace: sh-helm-test
+            app.kubernetes.io/part-of: ozgcloud
+            app.kubernetes.io/version: 0.0.0-MANAGED-BY-JENKINS
+            component: antragsraum-server-service
+            helm.sh/chart: antragsraum-server-0.0.0-MANAGED-BY-JENKINS
+
+  - it: selector should contain helm recommended labels
+    asserts:
+      - equal:
+          path: spec.selector
+          value:
+            app.kubernetes.io/name: antragsraum-server
+            app.kubernetes.io/namespace: sh-helm-test
+            component: antragsraum-server
+
+  - it: spec should contain ports with correct value
+    asserts:
+      - equal:
+          path: spec.ports
+          value: 
+            - name: http
+              protocol: TCP
+              port: 8080
+              targetPort: 8080    
+            - name: management
+              protocol: TCP
+              port: 8081
+              targetPort: 8081
+  
+
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplicationITCase.java b/server/src/test/java/de/ozgcloud/antragsraum/AntragsRaumApplicationITCase.java
similarity index 85%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplicationITCase.java
rename to server/src/test/java/de/ozgcloud/antragsraum/AntragsRaumApplicationITCase.java
index c06f0c51eb74e94ac2c6dd4c24c5d7fe9e41f022..60c3436efe3cce6ba841608d8ab08f1d26b6f769 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplicationITCase.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/AntragsRaumApplicationITCase.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/AntragsRaumApplicationTest.java b/server/src/test/java/de/ozgcloud/antragsraum/AntragsRaumApplicationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..05830b0ecdf6856252a74c4c48b8403627333348
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/AntragsRaumApplicationTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023-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.antragsraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+@ExtendWith(MockitoExtension.class)
+class AntragsRaumApplicationTest {
+	@Spy
+	@InjectMocks
+	private AntragsRaumApplication application;
+
+	@Test
+	void shouldCreateThreadPoolTaskExecutor() {
+		var taskExecutor = mock(ThreadPoolTaskExecutorBuilder.class);
+		when(taskExecutor.build()).thenReturn(new ThreadPoolTaskExecutor());
+
+		var threadPool = application.taskExecutor(taskExecutor);
+
+		assertThat(threadPool).isNotNull().isInstanceOf(ThreadPoolTaskExecutor.class);
+	}
+
+	@Test
+	void shouldCreateCallScope() {
+		var callScope = application.callScope();
+
+		assertThat(callScope).isNotNull();
+	}
+
+	@Test
+	void shouldCreateBeanFactoryPostProcessor() {
+		var beanProcessor = AntragsRaumApplication.beanFactoryPostProcessor(new CallScope());
+
+		assertThat(beanProcessor).isNotNull();
+	}
+
+	@Test
+	void shouldCreateTrustResolver() {
+		var trustResolver = application.trustResolver();
+
+		assertThat(trustResolver).isNotNull();
+	}
+
+	@Test
+	void shouldAddSecurityContext() {
+		var customizer = application.addSecurityContextCustomizer();
+
+		assertThat(customizer).isNotNull();
+	}
+
+	@Test
+	void shouldSetThreadName() {
+		var customizer = application.setThreadName();
+
+		assertThat(customizer).isNotNull();
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/CallBeanFactoryPostProcessorTest.java b/server/src/test/java/de/ozgcloud/antragsraum/CallBeanFactoryPostProcessorTest.java
similarity index 89%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/CallBeanFactoryPostProcessorTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/CallBeanFactoryPostProcessorTest.java
index 79c8819cfd0c06fe549f6e1f3534898eea8dfcf2..fb5d9ec8f6864978479202e8dabf455fd9ac1a71 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/CallBeanFactoryPostProcessorTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/CallBeanFactoryPostProcessorTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/CallScopeTest.java b/server/src/test/java/de/ozgcloud/antragsraum/CallScopeTest.java
similarity index 96%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/CallScopeTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/CallScopeTest.java
index 5c1a7bef60c2b251b94d91e097b701ed152a2411..4866c0c868546913e23a0061cda870ba974557f1 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/CallScopeTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/CallScopeTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/GrpcTestConfig.java b/server/src/test/java/de/ozgcloud/antragsraum/GrpcTestConfig.java
similarity index 82%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/GrpcTestConfig.java
rename to server/src/test/java/de/ozgcloud/antragsraum/GrpcTestConfig.java
index c47e412f6c3254f3707c0e6ead51710be0693915..ed3600ae442b752848b914c755d4b6764920e62b 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/GrpcTestConfig.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/GrpcTestConfig.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -20,8 +17,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import net.devh.boot.grpc.client.autoconfigure.*;
 import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
@@ -37,7 +33,6 @@ import org.springframework.context.annotation.Configuration;
         GrpcClientAutoConfiguration.class,
         GrpcClientHealthAutoConfiguration.class,
         GrpcClientMetricAutoConfiguration.class,
-        GrpcClientSecurityAutoConfiguration.class,
         GrpcClientTraceAutoConfiguration.class,
         GrpcDiscoveryClientAutoConfiguration.class,
 })
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/ModularityTests.java b/server/src/test/java/de/ozgcloud/antragsraum/ModularityTests.java
similarity index 87%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/ModularityTests.java
rename to server/src/test/java/de/ozgcloud/antragsraum/ModularityTests.java
index 73be5eeb01565e07efc1aa3de23cdf659cc1283b..e87d78b8c44351f427135d976be076910329b914 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/ModularityTests.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/ModularityTests.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import org.junit.jupiter.api.Test;
 import org.springframework.modulith.core.ApplicationModules;
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/RootControllerTest.java b/server/src/test/java/de/ozgcloud/antragsraum/RootControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a1aeaf65334a71317262be0c359a2bccd763abc
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/RootControllerTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.antragsraum;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.http.HttpStatusCode;
+
+import java.time.Instant;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class RootControllerTest {
+
+	@Nested
+	class TestWithBuildProperties {
+		@Mock
+		private BuildProperties buildProperties;
+
+		@InjectMocks
+		private RootController rootController;
+
+		@Test
+		void shouldHaveStatusOk() {
+			var response = rootController.getRoot();
+
+			assertThat(response.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(200));
+		}
+
+		@Test
+		void shouldHaveBody() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root).isNotNull();
+		}
+
+		@Test
+		void shouldHaveBuildVersion() {
+			when(buildProperties.getVersion()).thenReturn("123");
+
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.version()).isEqualTo("123");
+		}
+
+		@Test
+		void shouldHaveBuildVersionDefault() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.version()).isNull();
+		}
+
+		@Test
+		void shouldHaveBuildTime() {
+			Instant buildTime = Instant.now();
+			when(buildProperties.getTime()).thenReturn(buildTime);
+
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.buildTime()).isEqualTo(buildTime);
+		}
+
+		@Test
+		void shouldHaveBuildTimeDefault() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.buildTime()).isNull();
+		}
+
+		@Test
+		void shouldHaveName() {
+			when(buildProperties.getName()).thenReturn("Antragsraum");
+
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.name()).isEqualTo("Antragsraum");
+		}
+
+		@Test
+		void shouldHaveNameDefault() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.name()).isNull();
+		}
+
+		@Test
+		void shouldHaveJavaVersion() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.javaVersion()).isNotEmpty();
+		}
+
+		@Test
+		void shouldProduction() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.isProduction()).isEqualTo(true);
+		}
+	}
+
+	@Nested
+	class TestWithOutBuildProperties {
+		private final RootController rootController = new RootController(null);
+
+		@Test
+		void shouldHaveStatusOk() {
+			var response = rootController.getRoot();
+
+			assertThat(response.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(200));
+		}
+
+		@Test
+		void shouldHaveBody() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root).isNotNull();
+		}
+
+		@Test
+		void shouldHaveBuildVersionDefault() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.version()).isNull();
+		}
+
+		@Test
+		void shouldHaveBuildTimeDefault() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.buildTime()).isNull();
+		}
+
+		@Test
+		void shouldHaveNameDefault() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.name()).isNull();
+		}
+
+		@Test
+		void shouldHaveJavaVersion() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.javaVersion()).isNotEmpty();
+		}
+
+		@Test
+		void shouldProduction() {
+			var root = rootController.getRoot().getBody();
+
+			assertThat(root.isProduction()).isEqualTo(true);
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/SecurityConfigurationTest.java b/server/src/test/java/de/ozgcloud/antragsraum/SecurityConfigurationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5dd33d55261b79126222c1e512060b55b2677f36
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/SecurityConfigurationTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2023-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.antragsraum;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.UUID;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import de.ozgcloud.antragsraum.security.AntragsraumLogoutSuccessHandler;
+import de.ozgcloud.antragsraum.security.AntragsraumProperties;
+import de.ozgcloud.antragsraum.security.BayernIdProperties;
+import de.ozgcloud.antragsraum.security.BayernIdSaml2Extension;
+import de.ozgcloud.antragsraum.security.InMemoryUserDetailService;
+import de.ozgcloud.antragsraum.security.JwtTokenFilter;
+import de.ozgcloud.antragsraum.security.SamlUrlAuthenticationSuccessHandler;
+
+@ExtendWith(MockitoExtension.class)
+class SecurityConfigurationTest {
+	@Mock
+	private BayernIdSaml2Extension bayernIdSaml2Extension;
+
+	@Mock
+	private BayernIdProperties properties;
+
+	@Mock
+	JwtTokenFilter tokenFilter;
+
+	@Mock
+	InMemoryUserDetailService userDetailsService;
+
+	@Mock
+	AntragsraumProperties corsProperties;
+
+	private SecurityConfiguration securityConfiguration;
+
+	@BeforeEach
+	void setUp() {
+		securityConfiguration = new SecurityConfiguration(bayernIdSaml2Extension, properties, userDetailsService, tokenFilter, corsProperties);
+	}
+
+	@Nested
+	class TestHttpSecurity {
+		private HttpSecurity httpSecurity;
+
+		@BeforeEach
+		void init() throws Exception {
+			httpSecurity = mock(HttpSecurity.class);
+			when(httpSecurity.authorizeHttpRequests(any())).thenReturn(httpSecurity);
+			when(httpSecurity.saml2Login(any())).thenReturn(httpSecurity);
+			when(httpSecurity.saml2Logout(any())).thenReturn(httpSecurity);
+			when(httpSecurity.addFilterBefore(any(), any())).thenReturn(httpSecurity);
+			when(httpSecurity.csrf(any())).thenReturn(httpSecurity);
+		}
+
+		@Test
+		void shouldSetupFilterChainAuthorize() throws Exception {
+			securityConfiguration.filterChain(httpSecurity);
+
+			verify(httpSecurity).authorizeHttpRequests(any());
+		}
+
+		@Test
+		void shouldSetupFilterChainSaml2Login() throws Exception {
+			securityConfiguration.filterChain(httpSecurity);
+
+			verify(httpSecurity).saml2Login(any());
+		}
+
+		@Test
+		void shouldSetupFilterChainSaml2Logout() throws Exception {
+			securityConfiguration.filterChain(httpSecurity);
+
+			verify(httpSecurity).saml2Logout(any());
+		}
+	}
+
+	@Nested
+	class TestAuthenticationRequestResolver {
+		@Mock
+		private RelyingPartyRegistrationRepository registrations;
+
+		@Test
+		void shouldCreateSaml2AuthenticationRequestResolver() {
+			var authResolver = securityConfiguration.authenticationRequestResolver(registrations);
+
+			assertThat(authResolver).isInstanceOf(Saml2AuthenticationRequestResolver.class);
+		}
+	}
+
+	@Nested
+	class TestAuthenticationConfiguration {
+		@Mock
+		private AuthenticationConfiguration authenticationConfiguration;
+
+		@Test
+		void shouldCreateAuthenticationManager() throws Exception {
+			when(authenticationConfiguration.getAuthenticationManager()).thenReturn(mock(AuthenticationManager.class));
+
+			var authenticationManager = securityConfiguration.authenticationManager(authenticationConfiguration);
+
+			assertThat(authenticationManager).isInstanceOf(AuthenticationManager.class);
+		}
+
+		@Test
+		void shouldCreateMockAuthenticationManager() throws Exception {
+			ReflectionTestUtils.setField(securityConfiguration, "mockAuth", true);
+
+			var authenticationManager = securityConfiguration.authenticationManager(authenticationConfiguration);
+
+			assertThat(authenticationManager).isInstanceOf(ProviderManager.class);
+		}
+	}
+
+	@Nested
+	class TestSamlFilterChain {
+		@Mock
+		HttpSecurity security;
+
+		@BeforeEach
+		void setUp() throws Exception {
+			ReflectionTestUtils.setField(securityConfiguration, "mockAuth", false);
+			when(security.authorizeHttpRequests(any())).thenReturn(security);
+			when(security.addFilterBefore(any(), any())).thenReturn(security);
+			when(security.saml2Login(any())).thenReturn(security);
+			when(security.saml2Logout(any())).thenReturn(security);
+		}
+
+		@Test
+		void shouldSetSamlLogin() throws Exception {
+			securityConfiguration.filterChain(security);
+
+			verify(security).saml2Login(any());
+		}
+
+		@Test
+		void shouldSetSamlLogout() throws Exception {
+			securityConfiguration.filterChain(security);
+
+			verify(security).saml2Logout(any());
+		}
+	}
+
+	@Nested
+	class TestMockFilterChain {
+		@Mock
+		HttpSecurity security;
+
+		@BeforeEach
+		void setUp() throws Exception {
+			ReflectionTestUtils.setField(securityConfiguration, "mockAuth", true);
+			ReflectionTestUtils.setField(securityConfiguration, "password", UUID.randomUUID().toString());
+
+			when(security.authorizeHttpRequests(any())).thenReturn(security);
+			when(security.addFilterBefore(any(), any())).thenReturn(security);
+			when(security.httpBasic(any())).thenReturn(security);
+			when(security.formLogin(any())).thenReturn(security);
+		}
+
+		@Test
+		void shouldSetHttpBasicIfMockAuth() throws Exception {
+			securityConfiguration.filterChain(security);
+
+			verify(security).httpBasic(any());
+		}
+	}
+
+	@Nested
+	class TestLogoutHandler {
+		@Test
+		void shouldCreateLogoutHandler() {
+			when(corsProperties.getLogoutSuccessUrl()).thenReturn("/");
+
+			var handler = securityConfiguration.logoutSuccessHandler();
+
+			assertThat(handler).isInstanceOf(AntragsraumLogoutSuccessHandler.class);
+		}
+	}
+
+	@Nested
+	class TestSuccessHandler {
+		@Test
+		void shouldCreateSuccessHandler() {
+			var handler = securityConfiguration.getSamlUrlAuthenticationSuccessHandler();
+
+			assertThat(handler).isInstanceOf(SamlUrlAuthenticationSuccessHandler.class);
+		}
+	}
+
+	@Nested
+	class TestCorsConfigurer {
+		@Test
+		void shouldCreateCorsConfigurer() {
+			var corsConfigurer = securityConfiguration.corsConfigurer();
+
+			assertThat(corsConfigurer).isInstanceOf(WebMvcConfigurer.class);
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/SwaggerCorsConfigurationTest.java b/server/src/test/java/de/ozgcloud/antragsraum/SwaggerCorsConfigurationTest.java
similarity index 86%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/SwaggerCorsConfigurationTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/SwaggerCorsConfigurationTest.java
index 96a770b9c594e18f69414af672f5699ac265b6eb..100fa9c4b6109be7d324d81107e539eef4e3d00e 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/SwaggerCorsConfigurationTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/SwaggerCorsConfigurationTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum;
+package de.ozgcloud.antragsraum;
 
 import org.junit.jupiter.api.Test;
 
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/WebConfigurationTest.java b/server/src/test/java/de/ozgcloud/antragsraum/WebConfigurationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..75d5c2033bc75fe6fd676e0a1a7568a0107d6b69
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/WebConfigurationTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.antragsraum;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.web.client.RestTemplate;
+
+class WebConfigurationTest {
+	@Test
+	void shouldCreateRestTemplate() {
+		WebConfiguration configuration = new WebConfiguration();
+		RestTemplate template = configuration.restTemplate(new RestTemplateBuilder());
+
+		assertThat(template).isNotNull();
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSenderTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSenderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..64ba29a6b8127ca300cb36c1a25b5f1d7287dca4
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/ChunkedFileSenderTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.antragsraum.attachments;
+
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest;
+import io.grpc.stub.ClientCallStreamObserver;
+
+@ExtendWith(MockitoExtension.class)
+class ChunkedFileSenderTest {
+
+	private ChunkedFileSender<GrpcUploadBinaryFileRequest> chunkedFileSender;
+
+	@Spy
+	InputStream inputStream = OzgUploadFileTestFactory.create().fileContent();
+
+	@BeforeEach
+	void setUp() {
+		var metaDataRequest = GrpcUploadBinaryFileRequest.newBuilder().build();
+
+		chunkedFileSender = spy(new ChunkedFileSender<>(inputStream, FileGrpcClient.CHUNK_SIZE,
+		  this::buildChunkRequest, metaDataRequest));
+	}
+
+	GrpcUploadBinaryFileRequest buildChunkRequest(byte[] bytes) {
+		return GrpcUploadBinaryFileRequest.newBuilder().setFileContent((ByteString.copyFrom(bytes))).build();
+	}
+
+	@Nested
+	class TestSendingFileChunks {
+		@Test
+		@SuppressWarnings("unchecked")
+		void shouldCallOnNext() {
+			var observer = mock(ClientCallStreamObserver.class);
+
+			chunkedFileSender.sendChunkTo(observer);
+
+			verify(observer, atLeast(1)).onNext(any());
+		}
+
+		@Test
+		@SuppressWarnings("unchecked")
+		void shouldCallOnCompleted() {
+			var observer = mock(ClientCallStreamObserver.class);
+
+			chunkedFileSender.sendChunkTo(observer);
+
+			verify(observer).onCompleted();
+		}
+
+		@Test
+		@SuppressWarnings("unchecked")
+		void shouldCallReadNBytes() throws IOException {
+			var observer = mock(ClientCallStreamObserver.class);
+
+			chunkedFileSender.sendChunkTo(observer);
+
+			verify(inputStream).readNBytes(anyInt());
+		}
+
+		@Test
+		@SuppressWarnings("unchecked")
+		void shouldCallInputStreamClose() throws IOException {
+			var observer = mock(ClientCallStreamObserver.class);
+
+			chunkedFileSender.sendChunkTo(observer);
+
+			verify(inputStream).close();
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerITCase.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerITCase.java
similarity index 57%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerITCase.java
rename to server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerITCase.java
index 8f21aafbff22ef29b4a2b8be699669955f17ce00..7908af6f64880c6e528111daec5fb401c466f4d1 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerITCase.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerITCase.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,14 +18,36 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
-
-import com.c4_soft.springaddons.security.oauth2.test.annotations.WithJwt;
-import de.mgm.bup.ozg.antragsraum.Oauth2TestConfiguration;
-import de.mgm.bup.ozg.antragsraum.common.NotFoundException;
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import de.mgm.bup.ozg.antragsraum.common.VirusFoundException;
-import de.mgm.bup.ozg.antragsraum.events.NachrichtEventService;
+package de.ozgcloud.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileController.PATH;
+import static de.ozgcloud.antragsraum.attachments.FileController.PATH_PATTERN;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.ENCODED_CHANNEL_ADDRESS;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_ID;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.VORGANG_ID;
+import static de.ozgcloud.antragsraum.attachments.FileValidatorTest.TEST_FILE;
+import static de.ozgcloud.antragsraum.attachments.FileValidatorTest.TEST_INVALID_FILE;
+import static de.ozgcloud.antragsraum.attachments.FileValidatorTest.TEST_TOO_LARGE_FILE;
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import de.ozgcloud.antragsraum.common.NotFoundException;
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.antragsraum.common.VirusFoundException;
+import de.ozgcloud.antragsraum.nachricht.NachrichtTestFactory;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.concurrent.CompletableFuture;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -37,28 +56,19 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.ResultActions;
-
-import java.util.concurrent.CompletableFuture;
-
-import static de.mgm.bup.ozg.antragsraum.attachments.FileController.*;
-import static de.mgm.bup.ozg.antragsraum.attachments.OzgFileTestFactory.*;
-import static org.hamcrest.Matchers.*;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.*;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import org.springframework.web.client.RestTemplate;
 
 @AutoConfigureMockMvc
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
-@SpringJUnitConfig(classes = {FileControllerTestConfiguration.class, Oauth2TestConfiguration.class})
+@SpringJUnitConfig(classes = {FileControllerTestConfiguration.class})
 public class FileControllerITCase {
     @SpyBean
     private FileController controller;
@@ -67,7 +77,7 @@ public class FileControllerITCase {
     private FileService fileService;
 
     @MockBean
-    private NachrichtEventService nachrichtEventService;
+    private RestTemplate restTemplate;
 
     @Autowired
     private MockMvc mockMvc;
@@ -76,12 +86,11 @@ public class FileControllerITCase {
     class TestDownloadingFileContent {
         @BeforeEach
         void init() {
-            when(fileService.getFile(anyString(), anyString())).thenReturn(OzgFileTestFactory.create());
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(NACHRICHT_ID);
+            when(fileService.getFile(anyString(), anyString())).thenReturn(OzgFileTestFactory.createOzgFile());
         }
 
         @Test
-        @WithJwt("user.json")
+        @WithMockUser
         void shouldGetFileContent() throws Exception {
             performRequest().andExpect(status().isOk());
         }
@@ -91,12 +100,11 @@ public class FileControllerITCase {
     class TestFileNotFoundError {
         @BeforeEach
         void init() {
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(NACHRICHT_ID);
             doThrow(NotFoundException.class).when(fileService).getFile(anyString(), anyString());
         }
 
         @Test
-        @WithJwt("user.json")
+        @WithMockUser
         void shouldReturn404() throws Exception {
             performRequest().andExpect(status().isNotFound());
         }
@@ -105,7 +113,7 @@ public class FileControllerITCase {
     @Nested
     class TestInternalError {
         @Test
-        @WithJwt("user.json")
+        @WithMockUser
         void shouldReturn500ByTechnicalException() throws Exception {
             doThrow(TechnicalException.class).when(fileService).getFile(anyString(), anyString());
 
@@ -113,7 +121,7 @@ public class FileControllerITCase {
         }
 
         @Test
-        @WithJwt("user.json")
+        @WithMockUser
         void shouldReturn500ByRuntimeException() throws Exception {
             doThrow(RuntimeException.class).when(fileService).getFile(anyString(), anyString());
 
@@ -125,7 +133,7 @@ public class FileControllerITCase {
     class TestNotAcceptable {
         @BeforeEach
         void init() {
-            when(fileService.getFile(anyString(), any())).thenReturn(OzgFileTestFactory.create());
+            when(fileService.getFile(anyString(), any())).thenReturn(OzgFileTestFactory.createOzgFile());
             doThrow(VirusFoundException.class).when(fileService).getFile(anyString(), any());
         }
 
@@ -137,10 +145,11 @@ public class FileControllerITCase {
     }
 
     ResultActions performRequest() throws Exception {
+        var id = Base64.getEncoder().encodeToString(NachrichtTestFactory.ADDRESS.getBytes(StandardCharsets.UTF_8));
         return mockMvc.perform(
-                get(PATH + NACHRICHT_ID + "/" + FILE_ID)
-                        .with(csrf().asHeader())
-                        .accept(MediaType.APPLICATION_OCTET_STREAM_VALUE));
+            get(PATH + id + "/" + FILE_ID)
+                .with(csrf().asHeader())
+                .accept(MediaType.APPLICATION_OCTET_STREAM_VALUE));
     }
 
     @Nested
@@ -149,34 +158,51 @@ public class FileControllerITCase {
 
         @BeforeEach
         void init() {
-            when(nachrichtEventService.getServiceUrlOfNachricht(anyString())).thenReturn(NACHRICHT_ID);
             when(fileService.upload(anyString(), anyString(), any())).thenReturn(fileIdFuture);
         }
 
         @Test
-        @WithJwt("user.json")
+        @WithMockUser
         void shouldReturnCreatedStatus() throws Exception {
-            var result = callEndpoint();
+            var result = callEndpoint(TEST_FILE);
 
             mockMvc.perform(asyncDispatch(result)).andExpect(status().isCreated());
         }
 
         @Test
-        @WithJwt("user.json")
+        @WithMockUser
         void shouldReturnLocation() throws Exception {
-            var result = callEndpoint();
+            var result = callEndpoint(TEST_FILE);
+
+            mockMvc.perform(asyncDispatch(result))
+                .andExpect(header().string("Location", containsString(PATH_PATTERN.formatted(VORGANG_ID, FILE_ID))));
+        }
+
+        @Test
+        @WithMockUser
+        void shouldReturnNotAcceptableBecauseOfFileType() throws Exception {
+            var result = callEndpoint(TEST_INVALID_FILE);
+
+            mockMvc.perform(asyncDispatch(result))
+                .andExpect(status().is(HttpStatus.NOT_ACCEPTABLE.value()));
+        }
+
+        @Test
+        @WithMockUser
+        void shouldReturnNotAcceptableBecauseOfFileSize() throws Exception {
+            var result = callEndpoint(TEST_TOO_LARGE_FILE);
 
             mockMvc.perform(asyncDispatch(result))
-                    .andExpect(header().string("Location", containsString(PATH_PATTERN.formatted(VORGANG_ID, FILE_ID))));
+                .andExpect(status().is(HttpStatus.NOT_ACCEPTABLE.value()));
         }
 
-        private MvcResult callEndpoint() throws Exception {
-            return mockMvc.perform(multipart(PATH_PATTERN.formatted(VORGANG_ID, FILE_ID))
-                            .file(TEST_FILE)
-                            .with(csrf())
-                            .contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
-                    .andExpect(request().asyncStarted())
-                    .andReturn();
+        private MvcResult callEndpoint(MockMultipartFile file) throws Exception {
+            return mockMvc.perform(multipart(PATH_PATTERN.formatted(VORGANG_ID, ENCODED_CHANNEL_ADDRESS))
+                    .file(file)
+                    .with(csrf())
+                    .contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
+                .andExpect(request().asyncStarted())
+                .andReturn();
         }
     }
 }
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9c4c9556eec332ff4cda6dfb6d6fa2f59ed8597
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.VORGANG_ID;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.*;
+import static de.ozgcloud.antragsraum.nachricht.NachrichtTestFactory.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.concurrent.CompletableFuture;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.MediaType;
+import org.springframework.web.multipart.MultipartFile;
+
+@ExtendWith(MockitoExtension.class)
+class FileControllerTest {
+	@Spy
+	@InjectMocks
+	private FileController fileController;
+
+	@Mock
+	private FileService fileService;
+
+	@Mock
+	private FileValidator fileValidator;
+
+	@Nested
+	class TestGetFileContent {
+		@BeforeEach
+		void init() {
+			when(fileService.getFile(anyString(), anyString())).thenReturn(OzgFileTestFactory.createOzgFile());
+		}
+
+		@Test
+		void shouldCallGetFile() {
+			fileController.getFileData(
+			  Base64.getEncoder().encodeToString(CHANNEL_ADDRESS.getBytes(StandardCharsets.UTF_8)), FILE_ID);
+
+			verify(fileService).getFile(anyString(), anyString());
+		}
+
+		@Test
+		void shouldHaveStatusOk() {
+			var response = fileController.getFileData(
+			  Base64.getEncoder().encodeToString(CHANNEL_ADDRESS.getBytes(StandardCharsets.UTF_8)), FILE_ID);
+
+			assertThat(response.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(HttpStatus.OK.value()));
+		}
+
+		@Test
+		void shouldHaveContentLength() {
+			var response = fileController.getFileData(
+			  Base64.getEncoder().encodeToString(CHANNEL_ADDRESS.getBytes(StandardCharsets.UTF_8)), FILE_ID);
+
+			assertThat(response.getHeaders().getContentLength()).isEqualTo(FILE_SIZE);
+		}
+
+		@Test
+		void shouldHaveContentType() {
+			var response = fileController.getFileData(
+			  Base64.getEncoder().encodeToString(CHANNEL_ADDRESS.getBytes(StandardCharsets.UTF_8)), FILE_ID);
+
+			assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.parseMediaType(CONTENT_TYPE));
+		}
+
+		@Test
+		void shouldHandleNullFile() {
+			when(fileService.getFile(anyString(), anyString())).thenReturn(null);
+
+			var response = fileController.getFileData(
+			  Base64.getEncoder().encodeToString(CHANNEL_ADDRESS.getBytes(StandardCharsets.UTF_8)), FILE_ID);
+
+			assertThat(response.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(404));
+		}
+
+		@Test
+		void shouldHaveContentDispositionHeader() {
+			var contentDisposition = fileController.getFileData(
+				Base64.getEncoder().encodeToString(CHANNEL_ADDRESS.getBytes(StandardCharsets.UTF_8)), FILE_ID)
+			  .getHeaders().getContentDisposition();
+
+			assertThat(contentDisposition.toString()).isEqualTo("attachment; filename=\"" + FILE_NAME + "\"");
+		}
+	}
+
+	@Nested
+	class TestGetFile {
+		@Test
+		void shouldCallService() {
+			fileController.getFile(FILE_ID, REPLY_TO_NACHRICHT_ID);
+
+			verify(fileService).getFile(any(), anyString());
+		}
+	}
+
+	@Nested
+	class TestFileUpload {
+		private final CompletableFuture<String> fileIdFuture = CompletableFuture.completedFuture(FILE_ID);
+
+		@Mock
+		private MultipartFile multipartFile;
+
+		@BeforeEach
+		void init() {
+			when(fileService.upload(anyString(), anyString(), any())).thenReturn(fileIdFuture);
+			when(fileValidator.isValid(any())).thenReturn(true);
+		}
+
+		@Test
+		void shouldCallService() {
+			fileController.uploadFile(VORGANG_ID, ENCODED_CHANNEL_ADDRESS, multipartFile);
+
+			verify(fileService).upload(anyString(), anyString(), any(MultipartFile.class));
+		}
+	}
+
+	@Nested
+	class TestFileUploadExceptions {
+
+		@Mock
+		private MultipartFile multipartFile;
+
+		@Test
+		void shouldThrowExceptionWhenVorgangIdIsNull() {
+			assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(
+			  () -> fileController.uploadFile(null, ENCODED_CHANNEL_ADDRESS, multipartFile));
+		}
+
+		@Test
+		void shouldThrowExceptionWhenRueckfrageIdIsNull() {
+			assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(
+			  () -> fileController.uploadFile(VORGANG_ID, null, multipartFile));
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerTestConfiguration.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerTestConfiguration.java
similarity index 66%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerTestConfiguration.java
rename to server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerTestConfiguration.java
index 66857e788ecea95c943fa01edca343dcc92a8bb1..7a3b08d5e85a4b7a6e21d7beff44df111341b2e4 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileControllerTestConfiguration.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileControllerTestConfiguration.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,10 +20,13 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
 
+import de.ozgcloud.apilib.common.callcontext.DefaultOzgCloudCallContextProvider;
+import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider;
 import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
 import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
@@ -35,9 +37,10 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
 @EnableWebMvc
 @EnableAsync
-@ComponentScan(value = {"de.mgm.bup.ozg.antragsraum.attachments", "de.mgm.bup.ozg.antragsraum.common", "de.mgm.bup.ozg.antragsraum.callcontext"},
-        excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
-                classes = FileGrpcTestConfiguration.class))
+@ComponentScan(value = {"de.ozgcloud.antragsraum.attachments", "de.ozgcloud.antragsraum.common",
+    "de.ozgcloud.antragsraum.callcontext"},
+    excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
+        classes = FileGrpcTestConfiguration.class))
 @Configuration
 public class FileControllerTestConfiguration {
     @Bean
@@ -49,4 +52,9 @@ public class FileControllerTestConfiguration {
     GlobalClientInterceptorRegistry globalClientInterceptorRegistry(final ApplicationContext applicationContext) {
         return new GlobalClientInterceptorRegistry(applicationContext);
     }
+
+    @Bean
+    OzgCloudCallContextProvider contextProvider(@Autowired ApplicationContext applicationContext) {
+        return new DefaultOzgCloudCallContextProvider(applicationContext);
+    }
 }
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserverTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserverTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..81bad95b90409e28a294f5d0ed634cc8242ed44f
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileDownloadStreamObserverTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import com.google.protobuf.ByteString;
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataResponse;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.CompletableFuture;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.DATA;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class FileDownloadStreamObserverTest {
+	@InjectMocks
+	private FileDownloadStreamObserver downloadStreamObserver;
+	@Mock
+	private CompletableFuture<Boolean> streamFuture;
+	@Mock
+	private OutputStream out;
+
+	@Nested
+	class TestOnNext {
+
+		@Test
+		void shouldWriteToStreamOnReceivingContentPart() throws IOException {
+			downloadStreamObserver.onNext(createFileContentResponse());
+
+			verify(out).write(DATA);
+		}
+
+		private GrpcGetBinaryFileDataResponse createFileContentResponse() {
+			return GrpcGetBinaryFileDataResponse.newBuilder().setFileContent(ByteString.copyFrom(DATA)).build();
+		}
+
+		@Test
+		void shouldHandleException() throws IOException {
+			doThrow(IOException.class).when(out).write(any());
+
+			assertThatExceptionOfType(TechnicalException.class).isThrownBy(
+					() -> downloadStreamObserver.onNext(createFileContentResponse())).withMessage("TechnicalException happened! Message: null");
+		}
+	}
+
+	@Nested
+	class TestOnError {
+
+		@Mock
+		private Throwable throwable;
+
+		@Test
+		void shouldCompleteFutureExceptionally() {
+			downloadStreamObserver.onError(throwable);
+
+			verify(streamFuture).completeExceptionally(throwable);
+		}
+	}
+
+	@Nested
+	class TestOnCompleted {
+
+		@Test
+		void shouldCompleteFuture() {
+			downloadStreamObserver.onCompleted();
+
+			verify(streamFuture).complete(true);
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcClientTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcClientTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8533818a43ae77a1fe248ad1250c0a23627edc4
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcClientTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.common.ChannelPropertiesFactory;
+import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc;
+import de.ozgcloud.vorgang.grpc.command.GrpcCallContext;
+import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
+import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+
+@ExtendWith(MockitoExtension.class)
+class FileGrpcClientTest {
+	@Spy
+	@InjectMocks
+	private FileGrpcClient fileGrpcClient;
+
+	@Mock
+	private GlobalClientInterceptorRegistry registry;
+
+	@Mock
+	private ChannelPropertiesFactory channelPropertiesFactory;
+
+	@Mock
+	private ContextService contextService;
+
+	@Nested
+	class TestCreatingChannel {
+
+		@BeforeEach
+		void init() {
+			when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(
+			  new GrpcChannelsProperties());
+		}
+
+		@Test
+		void shouldGetChannelFactory() {
+			var channelFactory = fileGrpcClient.getChannelFactory(CHANNEL_ADDRESS);
+			assertThat(channelFactory).isNotNull();
+		}
+
+		@Test
+		void shouldGetChannel() {
+			when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
+
+			var channel = fileGrpcClient.getChannelFactory(CHANNEL_ADDRESS).createChannel(CHANNEL_ADDRESS);
+
+			assertThat(channel).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestDownloadObserver {
+		@Test
+		void shouldCreateDownloadObserver() {
+			var observer = fileGrpcClient.createDownloadFileObserver(CompletableFuture.completedFuture(Boolean.TRUE), new ByteArrayOutputStream());
+
+			assertThat(observer).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestUploadingFile {
+
+		@Mock
+		private BinaryFileServiceGrpc.BinaryFileServiceStub asyncStub;
+
+		private final OzgUploadFile file = OzgUploadFileTestFactory.create();
+
+		CompletableFuture<String> fileIdFuture = new CompletableFuture<>();
+
+		@BeforeEach
+		void init() {
+			when(contextService.createCallContext()).thenReturn(GrpcCallContext.newBuilder().build());
+			when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
+			when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(
+			  new GrpcChannelsProperties());
+		}
+
+		@Test
+		void shouldCallGetChannel() {
+			fileGrpcClient.uploadFile(file, CHANNEL_ADDRESS, fileIdFuture);
+
+			verify(fileGrpcClient).getChannelFactory(anyString());
+		}
+
+		@Test
+		void shouldCallBuildCallStreamObserver() {
+			fileGrpcClient.uploadFile(file, CHANNEL_ADDRESS, fileIdFuture);
+
+			verify(fileGrpcClient).createUploadFileObserver(any(), any());
+		}
+	}
+
+	@Nested
+	class TestBuildChunkRequest {
+
+		@Test
+		void shouldContainContent() {
+			var chunkRequest = fileGrpcClient.buildChunkRequest(DATA);
+
+			assertThat(chunkRequest.getFileContent()).isEqualTo(ByteString.copyFrom(DATA));
+		}
+	}
+
+	@Nested
+	class TestDownloadingFiles {
+
+		@Mock
+		private FileDownloadStreamObserver responseObserver;
+
+		@Mock
+		private BinaryFileServiceGrpc.BinaryFileServiceStub asyncStub;
+
+		private final ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+		@BeforeEach
+		void init() {
+			doReturn(responseObserver).when(fileGrpcClient).createDownloadFileObserver(any(), any(OutputStream.class));
+			doNothing().when(fileGrpcClient).waitUntilFutureToComplete(any());
+			when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
+			when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(
+			  new GrpcChannelsProperties());
+		}
+
+		@Test
+		void shouldCallCreateDownloadFileObserver() {
+			fileGrpcClient.downloadFileContent(FILE_ID, output, CHANNEL_ADDRESS);
+
+			verify(fileGrpcClient).createDownloadFileObserver(any(), any(OutputStream.class));
+		}
+	}
+
+	@Nested
+	class TestWaitUntilFutureToComplete {
+
+		@Mock
+		private CompletableFuture<Boolean> streamFuture;
+
+		@Test
+		void shouldNotThrowException() {
+			assertDoesNotThrow(() -> fileGrpcClient.waitUntilFutureToComplete(streamFuture));
+		}
+
+		@ParameterizedTest
+		@ValueSource(classes = { InterruptedException.class, ExecutionException.class, TimeoutException.class })
+		void shouldRethrowAsRuntimeException(Class<Exception> exception)
+		  throws InterruptedException, ExecutionException, TimeoutException {
+			doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class));
+
+			assertThrows(RuntimeException.class, () -> fileGrpcClient.waitUntilFutureToComplete(streamFuture));
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcTestConfiguration.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcTestConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd0ca0bbb4c5f6b78d9bf14e30628640c0231f1b
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileGrpcTestConfiguration.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static org.mockito.Mockito.*;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.google.common.collect.ImmutableList;
+
+import de.ozgcloud.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.common.AuthenticationHelper;
+import de.ozgcloud.antragsraum.common.ChannelPropertiesFactory;
+import lombok.NonNull;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientHealthAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientMetricAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientSecurityAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientTraceAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcDiscoveryClientAutoConfiguration;
+import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
+import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
+import net.devh.boot.grpc.common.autoconfigure.GrpcCommonTraceAutoConfiguration;
+import net.devh.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration;
+import net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration;
+
+@Configuration
+@ImportAutoConfiguration({
+  GrpcCommonCodecAutoConfiguration.class,
+  GrpcCommonTraceAutoConfiguration.class,
+
+  GrpcServerAutoConfiguration.class,
+  GrpcServerFactoryAutoConfiguration.class,
+
+  GrpcClientAutoConfiguration.class,
+  GrpcClientHealthAutoConfiguration.class,
+  GrpcClientMetricAutoConfiguration.class,
+  GrpcClientSecurityAutoConfiguration.class,
+  GrpcClientTraceAutoConfiguration.class,
+  GrpcDiscoveryClientAutoConfiguration.class
+})
+public class FileGrpcTestConfiguration {
+	@Bean
+	GrpcBinaryFileServiceStub grpcBinaryFileServiceStub() {
+		return new GrpcBinaryFileServiceStub();
+	}
+
+	@Bean
+	FileGrpcClient fileGrpcClient() {
+		return new FileGrpcClient(initGlobalClientInterceptorRegistry(), initChannelsPropertiesFactory(), contextProvider());
+	}
+
+	@Bean
+	FileRemoteService fileRemoteService(@Autowired FileGrpcClient fileGrpcClient) {
+		return new FileRemoteService(fileGrpcClient);
+	}
+
+	@NonNull
+	private static GlobalClientInterceptorRegistry initGlobalClientInterceptorRegistry() {
+		GlobalClientInterceptorRegistry globalClientInterceptorRegistry = mock(GlobalClientInterceptorRegistry.class);
+		when(globalClientInterceptorRegistry.getClientInterceptors()).thenReturn(ImmutableList.of());
+
+		return globalClientInterceptorRegistry;
+	}
+
+	@NonNull
+	private static ChannelPropertiesFactory initChannelsPropertiesFactory() {
+		return new ChannelPropertiesFactory(new GrpcChannelsProperties());
+	}
+
+	@NonNull
+	ContextService contextProvider() {
+		return new ContextService(new AuthenticationHelper());
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceITCase.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9529e2d0bd42c65cb8b1f082f363be5a268dc55
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceITCase.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.io.ByteArrayOutputStream;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import lombok.extern.log4j.Log4j2;
+
+@SpringBootTest(properties = {
+  "grpc.server.inProcessName=test3",
+  "grpc.server.port=-1"
+})
+@SpringJUnitConfig(classes = { FileGrpcTestConfiguration.class })
+@DirtiesContext
+@Log4j2
+public class FileRemoteServiceITCase {
+	private static final String CHANNEL_ADDRESS = "in-process:test3";
+
+	@SpyBean
+	private FileRemoteService fileRemoteService;
+
+	@Nested
+	class TestFileUpload {
+
+		@Test
+		@DirtiesContext
+		void shouldUploadFile() {
+			var idFuture = fileRemoteService.uploadFile(OzgUploadFileTestFactory.create(), CHANNEL_ADDRESS);
+
+			try {
+				assertThat(idFuture.get(1, TimeUnit.SECONDS)).isEqualTo(FileTestProperties.FILE_ID);
+			} catch (InterruptedException | ExecutionException | TimeoutException e) {
+				fail(e.getMessage());
+			}
+		}
+	}
+
+	@Nested
+	class TestGetFile {
+		@Test
+		@DirtiesContext
+		void shouldGetFile() {
+			var file = fileRemoteService.getFile(FileTestProperties.FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file).isNotNull();
+		}
+
+		@Test
+		@DirtiesContext
+		void shouldHaveFileMetaData() {
+			var file = fileRemoteService.getFile(FileTestProperties.FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file).isEqualTo(OzgFileTestFactory.createOzgFile());
+		}
+	}
+
+	@Nested
+	class TestGetFileContent {
+		@Test
+		@DirtiesContext
+		void shouldGetFileContent() {
+			var out = new ByteArrayOutputStream();
+			fileRemoteService.downloadFileContent(FileTestProperties.FILE_ID, out, CHANNEL_ADDRESS);
+
+			assertThat(out.size()).isEqualTo(FileTestProperties.DATA.length);
+		}
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5152deaa01fa5157b0e6ea18443e866c11e4890a
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileRemoteServiceTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcFindFilesResponse;
+import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile;
+import io.grpc.stub.StreamObserver;
+
+@ExtendWith(MockitoExtension.class)
+class FileRemoteServiceTest {
+	@Spy
+	@InjectMocks
+	private FileRemoteService remoteService;
+
+	@Mock
+	private FileGrpcClient grpcClient;
+
+	@Nested
+	class TestLoadingFile {
+
+		@BeforeEach
+		void init() {
+			var file = GrpcOzgFile.newBuilder()
+			  .setName(FILE_NAME)
+			  .setSize(FILE_SIZE)
+			  .setContentType(CONTENT_TYPE)
+			  .setId(FILE_ID).build();
+			when(grpcClient.findBinaryFilesMetaData(any(), any())).thenReturn(
+			  GrpcFindFilesResponse.newBuilder().addFile(file).build());
+		}
+
+		@Test
+		void shouldLoadFileMetadata() {
+			var file = remoteService.getFile(FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file).isNotNull();
+		}
+
+		@Test
+		void shouldHaveFileName() {
+			var file = remoteService.getFile(FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file.fileName()).isEqualTo(FILE_NAME);
+		}
+
+		@Test
+		void shouldHaveFileSize() {
+			var file = remoteService.getFile(FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file.fileSize()).isEqualTo(FILE_SIZE);
+		}
+
+		@Test
+		void shouldHaveContentType() {
+			var file = remoteService.getFile(FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file.contentType()).isEqualTo(CONTENT_TYPE);
+		}
+
+		@Test
+		void shouldHaveFileId() {
+			var file = remoteService.getFile(FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file.id()).isEqualTo(FILE_ID);
+		}
+
+		@Test
+		void shouldHandleEmpty() {
+			when(grpcClient.findBinaryFilesMetaData(any(), any())).thenReturn(
+			  GrpcFindFilesResponse.newBuilder().build());
+
+			var file = remoteService.getFile(FILE_ID, CHANNEL_ADDRESS);
+
+			assertThat(file).isNull();
+		}
+	}
+
+	@Nested
+	class TestDownloadFileContent {
+
+		private final ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+		@Test
+		void shouldCallDownloadFileObserver() {
+			remoteService.downloadFileContent(FILE_ID, output, CHANNEL_ADDRESS);
+
+			verify(grpcClient).downloadFileContent(eq(FILE_ID), any(OutputStream.class), eq(CHANNEL_ADDRESS));
+		}
+	}
+
+	@Nested
+	class TestUploadFile {
+
+		@Test
+		void shouldCallGrpcClient() {
+			var observer = mock(StreamObserver.class);
+
+			when(grpcClient.uploadFile(any(OzgUploadFile.class), eq(CHANNEL_ADDRESS), any())).thenReturn(observer);
+
+			remoteService.uploadFile(OzgUploadFileTestFactory.create(), CHANNEL_ADDRESS);
+
+			verify(grpcClient).uploadFile(any(OzgUploadFile.class), eq(CHANNEL_ADDRESS), any());
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileServiceTest.java
similarity index 60%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileServiceTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/attachments/FileServiceTest.java
index cc5868afc211f01238d7175dd204d11b9834a996..2165683c70abaf399beaf8c48b27c31701368bdb 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileServiceTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileServiceTest.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,10 +18,33 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
-
-import de.mgm.bup.ozg.antragsraum.common.TechnicalException;
-import de.mgm.bup.ozg.antragsraum.common.VirusFoundException;
+package de.ozgcloud.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.CHANNEL_ADDRESS;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.DATA;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_ID;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.VORGANG_ID;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.antragsraum.common.VirusFoundException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -35,22 +55,8 @@ import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import static de.mgm.bup.ozg.antragsraum.attachments.UploadOzgFileTestFactory.*;
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
 @ExtendWith(MockitoExtension.class)
 class FileServiceTest {
-    private static final String CHANNEL_ADDRESS = "inProcess:test";
     @Spy
     @InjectMocks
     private FileService fileService;
@@ -58,6 +64,9 @@ class FileServiceTest {
     @Mock
     private FileRemoteService remoteService;
 
+    @Mock
+    private VirusScannerClient virusScannerClient;
+
     @Nested
     class TestFileUpload {
 
@@ -66,23 +75,24 @@ class FileServiceTest {
 
         @BeforeEach
         void init() throws IOException {
-            when(remoteService.uploadFile(any(UploadOzgFile.class), anyString())).thenReturn(CompletableFuture.completedFuture(OzgFileTestFactory.FILE_ID));
-            when(multipartFile.getInputStream()).thenReturn(new ByteArrayInputStream(OzgFileTestFactory.DATA));
+            when(remoteService.uploadFile(any(OzgUploadFile.class), anyString())).thenReturn(
+                CompletableFuture.completedFuture(FILE_ID));
+            when(multipartFile.getInputStream()).thenReturn(new ByteArrayInputStream(DATA));
         }
 
         @Test
         void shouldCallRemoteService() {
-            fileService.upload(VORGANG_ID, REPLAY_TO_NACHRICHT_ID, multipartFile);
+            fileService.upload(VORGANG_ID, CHANNEL_ADDRESS, multipartFile);
 
-            verify(remoteService, times(1)).uploadFile(any(UploadOzgFile.class), anyString());
+            verify(remoteService, times(1)).uploadFile(any(OzgUploadFile.class), anyString());
         }
 
         @Test
         void shouldGetFileId() {
-            var res = fileService.upload(VORGANG_ID, REPLAY_TO_NACHRICHT_ID, multipartFile);
+            var res = fileService.upload(VORGANG_ID, CHANNEL_ADDRESS, multipartFile);
 
             try {
-                assertThat(res.get(5, TimeUnit.SECONDS)).isEqualTo(OzgFileTestFactory.FILE_ID);
+                assertThat(res.get(5, TimeUnit.SECONDS)).isEqualTo(FILE_ID);
             } catch (InterruptedException | ExecutionException | TimeoutException e) {
                 fail("Exception happened! " + e.getMessage());
             }
@@ -95,12 +105,12 @@ class FileServiceTest {
         private MultipartFile multipartFile;
 
         @Test
-        void shouldThrowRuntimeExceptionOnUpload() throws IOException {
+        void shouldThrowTechnicalExceptionOnUpload() throws IOException {
             doThrow(IOException.class).when(multipartFile).getInputStream();
 
-            assertThatExceptionOfType(RuntimeException.class).isThrownBy(
-                            () -> fileService.upload(VORGANG_ID, REPLAY_TO_NACHRICHT_ID, multipartFile))
-                    .withMessage("Error reading File data as stream.");
+            assertThatExceptionOfType(TechnicalException.class).isThrownBy(
+                    () -> fileService.upload(VORGANG_ID, CHANNEL_ADDRESS, multipartFile))
+                .withMessage("TechnicalException happened! Message: null");
         }
     }
 
@@ -109,12 +119,12 @@ class FileServiceTest {
 
         @BeforeEach
         void init() {
-            when(remoteService.getFile(anyString(), anyString())).thenReturn(OzgFileTestFactory.create());
+            when(remoteService.getFile(anyString(), anyString())).thenReturn(OzgFileTestFactory.createOzgFile());
         }
 
         @Test
         void shouldGetFile() {
-            var file = fileService.getFile(OzgFileTestFactory.FILE_ID, CHANNEL_ADDRESS);
+            var file = fileService.getFile(FILE_ID, CHANNEL_ADDRESS);
 
             assertThat(file).isNotNull();
         }
@@ -125,31 +135,23 @@ class FileServiceTest {
 
         @Test
         void shouldCallRemoteService() {
-            fileService.downloadFileContent(OzgFileTestFactory.FILE_ID, new ByteArrayOutputStream(), CHANNEL_ADDRESS);
+            fileService.downloadFileContent(FILE_ID, new ByteArrayOutputStream(), CHANNEL_ADDRESS);
 
             verify(remoteService).downloadFileContent(anyString(), any(OutputStream.class), anyString());
         }
     }
 
     @Nested
-    class TestVirusScanning{
+    class TestVirusScanning {
         @Mock
         private MultipartFile multipartFile;
 
-        @Test
-        void shouldThrowTechnicalExceptionOnScan() throws IOException {
-            doThrow(IOException.class).when(multipartFile).getBytes();
-
-            assertThatExceptionOfType(TechnicalException.class).isThrownBy(
-                            () -> fileService.isClean(multipartFile));
-        }
-
         @Test
         void shouldThrowVirusFoundExceptionWhenUploadingFile() {
-            when(fileService.isClean(multipartFile)).thenReturn(false);
+            when(virusScannerClient.scan(multipartFile)).thenReturn(Set.of("VirusA", "VirusB"));
 
             assertThatExceptionOfType(VirusFoundException.class).isThrownBy(
-                    () -> fileService.createUploadFile(VORGANG_ID, multipartFile));
+                () -> fileService.createUploadFile(VORGANG_ID, multipartFile));
         }
     }
 }
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileTestProperties.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileTestProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..6689f4110fa5bcc3fa057f62f5a74dad2abe2d8a
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileTestProperties.java
@@ -0,0 +1,38 @@
+/*
+ * 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.antragsraum.attachments;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.UUID;
+import org.apache.http.entity.ContentType;
+
+class FileTestProperties {
+    static final String FILE_ID = UUID.randomUUID().toString();
+    static final String VORGANG_ID = UUID.randomUUID().toString();
+    static final String CONTENT_TYPE = ContentType.APPLICATION_OCTET_STREAM.toString();
+    static final long FILE_SIZE = 10;
+    static final String FILE_NAME = "Testfile.pdf";
+    static final String CHANNEL_ADDRESS = "static://localhost:9090";
+    static final String ENCODED_CHANNEL_ADDRESS = Base64.getEncoder().encodeToString(CHANNEL_ADDRESS.getBytes(
+        StandardCharsets.UTF_8));
+    static final byte[] DATA = "juhu, ein bild".getBytes();
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeValidatorTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileValidatorTest.java
similarity index 51%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeValidatorTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/attachments/FileValidatorTest.java
index 6bdb405747ea6f3877d418ddf255098e6114d99a..5083e863ec5660dcbab54295ae26c4e1f113dc29 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/FileTypeValidatorTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/FileValidatorTest.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,7 +18,14 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.CONTENT_TYPE;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.DATA;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_NAME;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.Mockito.when;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
@@ -32,29 +36,35 @@ import org.junit.jupiter.params.provider.ValueSource;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.util.unit.DataSize;
 
 @ExtendWith(MockitoExtension.class)
-class FileTypeValidatorTest {
+class FileValidatorTest {
+    private static final String INVALID_FILE_NAME = "Testfile.exe";
+    public static final MockMultipartFile TEST_FILE = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA);
+    public static final MockMultipartFile TEST_INVALID_FILE =
+        new MockMultipartFile("file", INVALID_FILE_NAME, CONTENT_TYPE, DATA);
+    public static final MockMultipartFile TEST_TOO_LARGE_FILE =
+        new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, new byte[150_000_000]);
     @InjectMocks
-    private FileTypeValidator validator;
+    private FileValidator validator;
 
     @Mock
     private FileProperties fileProperties;
 
     @Nested
     class TestDefaultValues {
+
         @Nested
         class TestAllowedFileTypes {
 
             @ParameterizedTest
             @ValueSource(strings = {"pdf", "doc", "docx", "xls", "xlsx", "txt", "png", "jpg", "jpeg", "tif", "tiff"})
             void shouldBeValidLowerCase(String suffix) {
-                var file = UploadOzgFileTestFactory.createBuilderOfType("test." + suffix).build();
+                var file = createFile(suffix);
 
-                var result = validator.isValid(file, null);
+                var result = validator.isValid(file);
 
                 assertThat(result).isTrue();
             }
@@ -62,23 +72,41 @@ class FileTypeValidatorTest {
             @ParameterizedTest
             @ValueSource(strings = {"PDF"})
             void shouldBeValidUppercase(String suffix) {
-                var file = UploadOzgFileTestFactory.createBuilderOfType("test." + suffix).build();
+                var file = createFile(suffix);
 
-                var result = validator.isValid(file, null);
+                var result = validator.isValid(file);
 
                 assertThat(result).isTrue();
 
             }
         }
 
+        @Nested
+        class TestFileSize {
+
+            @Test
+            void shouldBeValidDefault() {
+                var res = validator.isValid(TEST_FILE);
+
+                assertThat(res).isTrue();
+            }
+
+            @Test
+            void shouldNotBeValid() {
+                var res = validator.isValid(TEST_INVALID_FILE);
+
+                assertThat(res).isFalse();
+            }
+        }
+
         @Nested
         class TestForbiddenFileTypes {
             @ParameterizedTest
             @ValueSource(strings = {"zip", "exe"})
             void shouldNotBeValidLowercase(String suffix) {
-                var file = UploadOzgFileTestFactory.createBuilderOfType("test." + suffix).build();
+                var file = createFile(suffix);
 
-                var result = validator.isValid(file, null);
+                var result = validator.isValid(file);
 
                 assertThat(result).isFalse();
             }
@@ -86,9 +114,9 @@ class FileTypeValidatorTest {
             @ParameterizedTest
             @ValueSource(strings = {"EXE"})
             void shouldNotBeValidUpperCase(String suffix) {
-                var file = UploadOzgFileTestFactory.createBuilderOfType("test." + suffix).build();
+                var file = createFile(suffix);
 
-                var result = validator.isValid(file, null);
+                var result = validator.isValid(file);
 
                 assertThat(result).isFalse();
             }
@@ -96,9 +124,9 @@ class FileTypeValidatorTest {
     }
 
     @Nested
-    class TestWithFileProperties {
+    class TestTypeWithFileProperties {
         @InjectMocks
-        private FileTypeValidator validator;
+        private FileValidator validator;
 
         @Mock
         private FileProperties fileProperties;
@@ -106,14 +134,15 @@ class FileTypeValidatorTest {
         @BeforeEach
         void init() {
             when(fileProperties.getAllowedSuffixes()).thenReturn("pdf,doc");
+            //when(fileProperties.getMaxFileSize()).thenReturn(DataSize.ofMegabytes(50));
         }
 
         @ParameterizedTest
         @ValueSource(strings = {"pdf", "doc"})
         void shouldBeValid(String suffix) {
-            var file = UploadOzgFileTestFactory.createBuilderOfType("test." + suffix).build();
+            var file = createFile(suffix);
 
-            var result = validator.isValid(file, null);
+            var result = validator.isValid(file);
 
             assertThat(result).isTrue();
         }
@@ -121,14 +150,40 @@ class FileTypeValidatorTest {
         @ParameterizedTest
         @ValueSource(strings = {"txt", "png"})
         void shouldNotBeValid(String suffix) {
-            var file = UploadOzgFileTestFactory.createBuilderOfType("test." + suffix).build();
+            var file = createFile(suffix);
 
-            var result = validator.isValid(file, null);
+            var result = validator.isValid(file);
 
             assertThat(result).isFalse();
         }
     }
 
+    @Nested
+    class TestSizeWithFileProperties {
+        @BeforeEach
+        void init() {
+            when(fileProperties.getMaxFileSize()).thenReturn(DataSize.ofMegabytes(50));
+        }
+
+        @Test
+        void shouldBeValidProperties() {
+            var res = validator.isValid(TEST_FILE);
+
+            assertThat(res).isTrue();
+        }
+
+        @Test
+        void shouldNotBeValidProperties() {
+            var res = validator.isValid(TEST_TOO_LARGE_FILE);
+
+            assertThat(res).isFalse();
+        }
+    }
+
+    private MockMultipartFile createFile(String suffix) {
+        return new MockMultipartFile("file", "test." + suffix, CONTENT_TYPE, DATA);
+    }
+
     @Test
     void shouldGetFileSuffix() {
         var suffix = validator.getFileNameSuffix("test.txt");
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/GrpcBinaryFileServiceStub.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/GrpcBinaryFileServiceStub.java
new file mode 100644
index 0000000000000000000000000000000000000000..51f85c311a80980f794b55cc0552bc0c7908a864
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/GrpcBinaryFileServiceStub.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.*;
+
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFilesRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcFindFilesResponse;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcGetBinaryFileDataResponse;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest;
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileResponse;
+import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile;
+import io.grpc.stub.CallStreamObserver;
+import io.grpc.stub.StreamObserver;
+import lombok.extern.log4j.Log4j2;
+import net.devh.boot.grpc.server.service.GrpcService;
+
+@GrpcService
+@Log4j2
+public class GrpcBinaryFileServiceStub extends BinaryFileServiceGrpc.BinaryFileServiceImplBase {
+	public void getBinaryFileContent(GrpcGetBinaryFileDataRequest request,
+	  StreamObserver<GrpcGetBinaryFileDataResponse> responseObserver) {
+		responseObserver.onNext(
+		  GrpcGetBinaryFileDataResponse.newBuilder().setFileContent(ByteString.copyFrom(DATA))
+			.build());
+		responseObserver.onCompleted();
+	}
+
+	public void findBinaryFilesMetaData(GrpcBinaryFilesRequest request,
+	  StreamObserver<GrpcFindFilesResponse> responseObserver) {
+		var file = GrpcOzgFile.newBuilder()
+		  .setContentType(CONTENT_TYPE)
+		  .setSize(FILE_SIZE)
+		  .setId(FILE_ID)
+		  .setName(FILE_NAME)
+		  .build();
+
+		responseObserver.onNext(GrpcFindFilesResponse.newBuilder().addFile(file).build());
+		responseObserver.onCompleted();
+	}
+
+	public StreamObserver<GrpcUploadBinaryFileRequest> uploadBinaryFileAsStream(StreamObserver<GrpcUploadBinaryFileResponse> responseObserver) {
+		return new CallStreamObserver<>() {
+
+			@Override
+			public boolean isReady() {
+				return true;
+			}
+
+			@Override
+			public void setOnReadyHandler(final Runnable onReadyHandler) {
+			}
+
+			@Override
+			public void disableAutoInboundFlowControl() {
+				// ignored
+			}
+
+			@Override
+			public void request(final int count) {
+				// ignore
+			}
+
+			@Override
+			public void setMessageCompression(final boolean enable) {
+				// ignored
+			}
+
+			@Override
+			public void onNext(final GrpcUploadBinaryFileRequest value) {
+				LOG.info("onNext called");
+			}
+
+			@Override
+			public void onError(final Throwable t) {
+				responseObserver.onError(t);
+			}
+
+			@Override
+			public void onCompleted() {
+				responseObserver.onNext(GrpcUploadBinaryFileResponse.newBuilder().setFileId(FILE_ID).build());
+				responseObserver.onCompleted();
+			}
+		};
+	}
+}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileMapperTest.java
similarity index 65%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileMapperTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileMapperTest.java
index bdc8e9a7cb9dfba8b782fb0f4eaaa66a30ed337a..397c760d170e6ef8fba0243ba952a43a99fda254 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/OzgFileMapperTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileMapperTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,13 +20,17 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+package de.ozgcloud.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.CONTENT_TYPE;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_ID;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_NAME;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_SIZE;
+import static org.assertj.core.api.Assertions.assertThat;
 
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
-import static org.assertj.core.api.Assertions.*;
-
 class OzgFileMapperTest {
 
     @Nested
@@ -36,28 +39,28 @@ class OzgFileMapperTest {
         void shouldHaveFileName() {
             var file = OzgFileMapper.fromGrpcFile(OzgFileTestFactory.createGrpcFile());
 
-            assertThat(file.fileName()).isEqualTo(OzgFileTestFactory.FILE_NAME);
+            assertThat(file.fileName()).isEqualTo(FILE_NAME);
         }
 
         @Test
         void shouldHaveFileSize() {
             var file = OzgFileMapper.fromGrpcFile(OzgFileTestFactory.createGrpcFile());
 
-            assertThat(file.fileSize()).isEqualTo(OzgFileTestFactory.FILE_SIZE);
+            assertThat(file.fileSize()).isEqualTo(FILE_SIZE);
         }
 
         @Test
         void shouldHaveContentType() {
             var file = OzgFileMapper.fromGrpcFile(OzgFileTestFactory.createGrpcFile());
 
-            assertThat(file.contentType()).isEqualTo(OzgFileTestFactory.CONTENT_TYPE);
+            assertThat(file.contentType()).isEqualTo(CONTENT_TYPE);
         }
 
         @Test
         void shouldHaveFileId() {
             var file = OzgFileMapper.fromGrpcFile(OzgFileTestFactory.createGrpcFile());
 
-            assertThat(file.id()).isEqualTo(OzgFileTestFactory.FILE_ID);
+            assertThat(file.id()).isEqualTo(FILE_ID);
         }
     }
 }
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..45aa7438ffba7982a3ed2e2acf3b2fb5d2b4e3f3
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgFileTestFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.CONTENT_TYPE;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_ID;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_NAME;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_SIZE;
+
+import de.ozgcloud.vorgang.grpc.file.GrpcOzgFile;
+
+public class OzgFileTestFactory {
+    public static OzgFile createOzgFile() {
+        return createOzgFileBuilder().build();
+    }
+
+    private static OzgFile.OzgFileBuilder createOzgFileBuilder() {
+        return OzgFile.builder()
+            .id(FILE_ID)
+            .contentType(CONTENT_TYPE)
+            .fileSize(FILE_SIZE)
+            .fileName(FILE_NAME);
+    }
+
+    static GrpcOzgFile createGrpcFile() {
+        return createGrpcFileBuilder().build();
+    }
+
+    private static GrpcOzgFile.Builder createGrpcFileBuilder() {
+        return GrpcOzgFile.newBuilder()
+            .setId(FILE_ID)
+            .setContentType(CONTENT_TYPE)
+            .setSize(FILE_SIZE)
+            .setName(FILE_NAME);
+    }
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgUploadFileTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgUploadFileTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8af49c925af63c5f883ed048934492abba915f8
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/OzgUploadFileTestFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.CONTENT_TYPE;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.DATA;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.FILE_NAME;
+import static de.ozgcloud.antragsraum.attachments.FileTestProperties.VORGANG_ID;
+
+import de.ozgcloud.apilib.file.OzgCloudUploadFile;
+import java.io.ByteArrayInputStream;
+
+class OzgUploadFileTestFactory {
+    static OzgUploadFile create() {
+        return createBuilder().build();
+    }
+
+    private static OzgUploadFile.OzgUploadFileBuilder createBuilder() {
+        return new OzgUploadFile.OzgUploadFileBuilder()
+            .fileData(OzgCloudUploadFile.builder()
+                .fileName(FILE_NAME)
+                .contentType(CONTENT_TYPE)
+                .vorgangId(VORGANG_ID)
+                .build())
+            .fileContent(new ByteArrayInputStream(DATA));
+    }
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/VirusScannerClientTest.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/VirusScannerClientTest.java
new file mode 100755
index 0000000000000000000000000000000000000000..2ffd3a934b5ff641a4b8a27446825eae5193162f
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/VirusScannerClientTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.web.client.RestTemplate;
+
+@ExtendWith(MockitoExtension.class)
+public class VirusScannerClientTest {
+    private static final String CLAMAV_SCAN_URL = "TestClamAVScanUrl";
+    private static final String FILE_NAME = "MockFileName.pdf";
+
+    @Spy
+    @InjectMocks
+    private VirusScannerClient virusScannerClient;
+
+    @Mock
+    private RestTemplate restTemplate;
+
+    @BeforeEach
+    void setup() {
+        ReflectionTestUtils.setField(virusScannerClient, "clamAVScanUrl", CLAMAV_SCAN_URL);
+    }
+
+    @Nested
+    class TestVirusScanNotSuccessful {
+
+        @Test
+        void shouldThrowRuntimeExceptionOnClamAVCall() {
+            var file = new MockMultipartFile(FILE_NAME, new byte[0]);
+            var status = HttpStatus.INTERNAL_SERVER_ERROR;
+            var response = new ResponseEntity<>(VirusScannerClientTestFactory.createVirusScanResponse(), status);
+
+            when(restTemplate.postForEntity(eq(CLAMAV_SCAN_URL), any(HttpEntity.class),
+                eq(VirusScannerClient.VirusScanResponse.class))).thenReturn(response);
+
+            assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> virusScannerClient.scan(file));
+        }
+
+        @Test
+        void shouldThrowRuntimeExceptionOnScan() {
+            var file = new MockMultipartFile(FILE_NAME, new byte[0]);
+            var status = HttpStatus.OK;
+            var response =
+                new ResponseEntity<>(VirusScannerClientTestFactory.createVirusScanResponse(false, false), status);
+
+            when(restTemplate.postForEntity(eq(CLAMAV_SCAN_URL), any(HttpEntity.class),
+                eq(VirusScannerClient.VirusScanResponse.class))).thenReturn(response);
+
+            assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> virusScannerClient.scan(file));
+        }
+    }
+
+    @Nested
+    class TestVirusScanSuccessful {
+
+        @Test
+        void shouldHaveTwoVirusesInResult() {
+            var file = new MockMultipartFile(FILE_NAME, new byte[0]);
+            var status = HttpStatus.OK;
+            var response =
+                new ResponseEntity<>(VirusScannerClientTestFactory.createVirusScanResponse(true, true), status);
+
+            when(restTemplate.postForEntity(eq(CLAMAV_SCAN_URL), any(HttpEntity.class),
+                eq(VirusScannerClient.VirusScanResponse.class))).thenReturn(response);
+
+            var result = virusScannerClient.scan(file);
+            assertThat(result).hasSize(2);
+        }
+
+        @Test
+        void shouldHaveEmptyResult() {
+            var file = new MockMultipartFile(FILE_NAME, new byte[0]);
+            var status = HttpStatus.OK;
+            var response = new ResponseEntity<>(VirusScannerClientTestFactory.createVirusScanResponse(), status);
+
+            when(restTemplate.postForEntity(eq(CLAMAV_SCAN_URL), any(HttpEntity.class),
+                eq(VirusScannerClient.VirusScanResponse.class))).thenReturn(response);
+
+            var result = virusScannerClient.scan(file);
+            assertThat(result).isEmpty();
+        }
+    }
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/attachments/VirusScannerClientTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/attachments/VirusScannerClientTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..29809c1e1da9b64940b679d471413bf465392420
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/attachments/VirusScannerClientTestFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.attachments;
+
+class VirusScannerClientTestFactory {
+    static final String FILE_NAME = "TestFileName.pdf";
+
+    static VirusScannerClient.VirusScanResponse createVirusScanResponse() {
+        return createVirusScanResponse(true, false);
+    }
+
+    static VirusScannerClient.VirusScanResponse createVirusScanResponse(boolean successful, boolean infected) {
+        VirusScannerClient.VirusScanResult result = new VirusScannerClient.VirusScanResult();
+        result.setName(FILE_NAME);
+        result.setInfected(infected);
+        result.setViruses(infected ? new String[] {"VirusA", "VirusB"} : new String[] {});
+
+        VirusScannerClient.VirusScanData data = new VirusScannerClient.VirusScanData();
+        data.setResult(new VirusScannerClient.VirusScanResult[] {result});
+
+        VirusScannerClient.VirusScanResponse response = new VirusScannerClient.VirusScanResponse();
+        response.setData(data);
+        response.setSuccess(successful);
+
+        return response;
+    }
+}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/callcontext/CallContextAttachingInterceptorTest.java b/server/src/test/java/de/ozgcloud/antragsraum/callcontext/CallContextAttachingInterceptorTest.java
similarity index 89%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/callcontext/CallContextAttachingInterceptorTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/callcontext/CallContextAttachingInterceptorTest.java
index ef55c1899e5476dd96270064302e7c507ccff610..56fd22ebc83d83ecd75bd343d5c6191b8707de9d 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/callcontext/CallContextAttachingInterceptorTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/callcontext/CallContextAttachingInterceptorTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,10 +19,9 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
+package de.ozgcloud.antragsraum.callcontext;
 
-package de.mgm.bup.ozg.antragsraum.callcontext;
-
-import de.mgm.bup.ozg.antragsraum.callcontext.CallContextAttachingInterceptor.CallContextAttachingClientCall;
+import de.ozgcloud.antragsraum.callcontext.CallContextAttachingInterceptor.CallContextAttachingClientCall;
 import io.grpc.Channel;
 import io.grpc.ClientCall;
 import io.grpc.Metadata;
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/callcontext/ContextServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/callcontext/ContextServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..102632443d8b2e2abaf9d12955aaa9597c720a45
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/callcontext/ContextServiceTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.callcontext;
+
+import de.ozgcloud.antragsraum.common.AuthenticationHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.test.context.support.WithMockUser;
+
+import static de.ozgcloud.antragsraum.callcontext.ContextService.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class ContextServiceTest {
+    public static final String ANTRAGSRAUM = "Antragsraum";
+    @Spy
+    @InjectMocks
+    private ContextService contextService;
+
+    @Mock
+    private AuthenticationHelper authenticationHelper;
+
+    @Nested
+    class TestCallContext {
+        @Test
+        void createCallContext() {
+            var grpcContext = contextService.createCallContext();
+
+            assertThat(grpcContext).isNotNull();
+        }
+
+        @Test
+        void getClientName() {
+            var name = contextService.getClientName();
+
+            assertThat(name).isEqualTo(ANTRAGSRAUM);
+        }
+
+        @Test
+        void getRequestId() {
+            var requestId = contextService.getRequestId();
+
+            assertThat(requestId).isNotNull();
+        }
+
+        @Test
+        void getEmptyRequestId() {
+            contextService.requestAttributes = null;
+
+            var requestId = contextService.getRequestId();
+
+            assertThat(requestId).isEqualTo("- no request scope -");
+        }
+
+        @Nested
+        class WithAuthentication {
+            @Test
+            void getOtherAuthenticationToken() {
+                setupToken();
+
+                var token = contextService.getSamlResponse();
+
+                assertThat(token).isEqualTo(NO_SAML_TOKEN);
+            }
+        }
+    }
+
+    @Nested
+    class TestCallContextMetadata {
+
+        @BeforeEach
+        void init() {
+            setupToken();
+        }
+
+        @Test
+        void buildCallContextMetadata() {
+            var metaData = contextService.buildCallContextMetadata();
+
+            assertThat(metaData).isNotNull();
+        }
+
+        @Test
+        @WithMockUser
+        void shouldHaveClientName() {
+            var metaData = contextService.buildCallContextMetadata();
+
+            assertThat(metaData.get(ContextService.createKeyOf(KEY_CLIENT_NAME)))
+                    .isEqualTo(CLIENT_NAME.getBytes());
+        }
+
+        @Test
+        void shouldHaveRequestId() {
+            var metaData = contextService.buildCallContextMetadata();
+
+            assertThat(metaData.get(ContextService.createKeyOf(KEY_REQUEST_ID)))
+                    .isNotNull();
+        }
+
+        @Test
+        void shouldHaveJwtToken() {
+            var metaData = contextService.buildCallContextMetadata();
+
+            assertThat(metaData.get(ContextService.createKeyOf(KEY_JWT_TOKEN)))
+                    .isNotNull();
+        }
+    }
+
+    private void setupToken() {
+        var authentication = mock(Authentication.class);
+        when(authenticationHelper.getAuthentication()).thenReturn(authentication);
+    }
+
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/AuthenticationHelperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/common/AuthenticationHelperTest.java
similarity index 93%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/AuthenticationHelperTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/common/AuthenticationHelperTest.java
index 04b2bf2ba33dc636a9f7a1714f7f3c709ea89487..8ac4bf1007844a06b34b6334089ab102fa7a59a6 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/AuthenticationHelperTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/common/AuthenticationHelperTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/ChannelPropertiesFactoryTest.java b/server/src/test/java/de/ozgcloud/antragsraum/common/ChannelPropertiesFactoryTest.java
similarity index 92%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/ChannelPropertiesFactoryTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/common/ChannelPropertiesFactoryTest.java
index 73700ca68447c6ca3f3e918ca0644ea792e97fc7..de724afac3a104774fd520b3d4c94538a52b73d6 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/ChannelPropertiesFactoryTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/common/ChannelPropertiesFactoryTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
 import net.devh.boot.grpc.client.config.NegotiationType;
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/common/InsufficientTrustLevelExceptionTest.java b/server/src/test/java/de/ozgcloud/antragsraum/common/InsufficientTrustLevelExceptionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7d3edc4e2a6def49a8a60a77816d390fe067ff3
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/common/InsufficientTrustLevelExceptionTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.UUID;
+
+import org.junit.jupiter.api.Test;
+
+class InsufficientTrustLevelExceptionTest {
+	@Test
+	void shouldCreateException() {
+		var rueckfrageId = UUID.randomUUID().toString();
+		var exception = new InsufficientTrustLevelException(rueckfrageId);
+
+		assertThat(exception.getMessage()).isEqualTo(
+		  "Current user does not have the required trust level for the Rueckfrage with id '" + rueckfrageId + "'.");
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/NotFoundExceptionTest.java b/server/src/test/java/de/ozgcloud/antragsraum/common/NotFoundExceptionTest.java
similarity index 86%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/NotFoundExceptionTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/common/NotFoundExceptionTest.java
index f80aef6e02ff7e07ee8350f947f9891101f4e24c..d2837d49881c9e4664d6c488742b7bdc31691ec5 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/NotFoundExceptionTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/common/NotFoundExceptionTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,8 +19,7 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum.common;
+package de.ozgcloud.antragsraum.common;
 
 import org.junit.jupiter.api.Test;
 
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/common/SecurityExceptionAdviceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/common/SecurityExceptionAdviceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..36be2b73108e9f4aa4d2d63fab7967d25e08f033
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/common/SecurityExceptionAdviceTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.antragsraum.common;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.*;
+
+class SecurityExceptionAdviceTest {
+    @Test
+    void shouldCreateException() {
+        var message = UUID.randomUUID().toString();
+
+        var exception = new SecurityException(message, new RuntimeException("test"));
+
+        assertThat(exception.getMessage()).isEqualTo(message);
+    }
+
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutExceptionTest.java b/server/src/test/java/de/ozgcloud/antragsraum/common/SendTimeoutExceptionTest.java
similarity index 84%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutExceptionTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/common/SendTimeoutExceptionTest.java
index 6a76d18d35660afa1b1f6b99ba56e0144ba6c62f..214bd6489c4373acab84799313461113b57c2cb2 100755
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/common/SendTimeoutExceptionTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/common/SendTimeoutExceptionTest.java
@@ -1,41 +1,39 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.common;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.UUID;
-
-import static org.assertj.core.api.Assertions.*;
-
-class SendTimeoutExceptionTest {
-
-    @Test
-    void shouldCreateException() {
-        var id = UUID.randomUUID().toString();
-        var exception = new SendTimeoutException(id, new RuntimeException("test"));
-
-        assertThat(exception.getMessage()).isEqualTo("A timeout sending a nachricht happened. Command " + id + " did not finish in time! Message: test");
-    }
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.*;
+
+class SendTimeoutExceptionTest {
+
+    @Test
+    void shouldCreateException() {
+        var id = UUID.randomUUID().toString();
+        var exception = new SendTimeoutException(id, new RuntimeException("test"));
+
+        assertThat(exception.getMessage()).isEqualTo("A timeout sending a nachricht happened. Command " + id + " did not finish in time! Message: test");
+    }
 }
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/common/VirusScanExceptionTest.java b/server/src/test/java/de/ozgcloud/antragsraum/common/VirusScanExceptionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..340b6336c0b7b29554fbb7630c5f151df077f519
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/common/VirusScanExceptionTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.common;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class VirusScanExceptionTest {
+	@Test
+	void shouldCreateException() {
+		var fileName = "TestFileName.pdf";
+		var exception = new VirusScanException(fileName);
+
+		assertThat(exception.getMessage()).isEqualTo("Virus scan for file with name '" + fileName + "' failed.");
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/GrpcNachrichtTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/events/GrpcNachrichtTestFactory.java
similarity index 63%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/GrpcNachrichtTestFactory.java
rename to server/src/test/java/de/ozgcloud/antragsraum/events/GrpcNachrichtTestFactory.java
index 34795c37afc83a9095b9de1a1e14e367cbb7a5c6..4565b306d4e90144ec9ce5c4678f66641a4c8c4c 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/GrpcNachrichtTestFactory.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/events/GrpcNachrichtTestFactory.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -21,21 +20,15 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.events;
-
-import de.mgm.bup.ozg.antragsraum.infomanager.GrpcNachricht;
+package de.ozgcloud.antragsraum.events;
 
+import de.ozgcloud.info.nachricht.GrpcNachricht;
 import java.util.UUID;
 
 class GrpcNachrichtTestFactory {
     static final String POSTFACH_ID = UUID.randomUUID().toString();
-    static final String VORGANG_ID = UUID.randomUUID().toString();
-    static final String NACHRICHTEN_ID = UUID.randomUUID().toString();
 
     static GrpcNachricht.Builder createBuilder() {
-        return GrpcNachricht.newBuilder()
-                .setPostfachId(POSTFACH_ID)
-                .setNachrichtId(NACHRICHTEN_ID)
-                .setVorgangId(VORGANG_ID);
+        return GrpcNachricht.newBuilder().setPostfachId(POSTFACH_ID);
     }
 }
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/InformationGrpcServiceStub.java b/server/src/test/java/de/ozgcloud/antragsraum/events/InformationGrpcServiceStub.java
similarity index 50%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/InformationGrpcServiceStub.java
rename to server/src/test/java/de/ozgcloud/antragsraum/events/InformationGrpcServiceStub.java
index 58d7326d61512d89a47d5b920ad3147a1ea74306..1da4b261c993fd5d81b0d1ee443f6e3d494ef1aa 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/InformationGrpcServiceStub.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/events/InformationGrpcServiceStub.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,10 +18,13 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.events;
+package de.ozgcloud.antragsraum.events;
 
-import de.mgm.bup.ozg.antragsraum.infomanager.*;
-import de.mgm.bup.ozg.antragsraum.nachricht.NachrichtEventTestFactory;
+import de.ozgcloud.antragsraum.nachricht.NachrichtEventTestFactory;
+import de.ozgcloud.info.GrpcInformationRequest;
+import de.ozgcloud.info.GrpcInformationResponse;
+import de.ozgcloud.info.InformationServiceGrpc;
+import de.ozgcloud.info.nachricht.GrpcNachricht;
 import io.grpc.stub.StreamObserver;
 import net.devh.boot.grpc.server.service.GrpcService;
 
@@ -32,33 +32,20 @@ import net.devh.boot.grpc.server.service.GrpcService;
 public class InformationGrpcServiceStub extends InformationServiceGrpc.InformationServiceImplBase {
     public static final NachrichtEvent NACHRICHT_EVENT = NachrichtEventTestFactory.create();
     public static final String EMPTY_POSTFACH_ID = "550e8400-e29b-41d4-a716-446655440000";
-    public static final String EMPTY_NACHRICHT_ID = "000e8400-e29b-41d4-a716-446655440000";
 
     @Override
-    public void getInformation(GrpcInformationRequest request, StreamObserver<GrpcInformationResponse> responseObserver) {
+    public void getInformation(GrpcInformationRequest request,
+                               StreamObserver<GrpcInformationResponse> responseObserver) {
         if (request.getPostfachId().equals(EMPTY_POSTFACH_ID)) {
             responseObserver.onNext(GrpcInformationResponse.getDefaultInstance());
         } else {
             responseObserver.onNext(GrpcInformationResponse.newBuilder()
-                    .addNachrichten(GrpcNachricht.newBuilder()
-                            .setPostfachId(NACHRICHT_EVENT.postfachId())
-                            .setNachrichtId(NACHRICHT_EVENT.rueckfrageId())
-                            .setVorgangId(NACHRICHT_EVENT.vorgangId())
-                            .build()).build()
+                .addNachrichten(GrpcNachricht.newBuilder()
+                    .setPostfachId(NACHRICHT_EVENT.postfachId())
+                    .build()).build()
             );
         }
 
         responseObserver.onCompleted();
     }
-
-    @Override
-    public void getServiceUrlOfNachricht(GrpcServiceUrlRequest request, StreamObserver<GrpcServiceUrlResponse> responseObserver) {
-        if (EMPTY_NACHRICHT_ID.equals(request.getNachrichtId())) {
-            responseObserver.onNext(GrpcServiceUrlResponse.getDefaultInstance());
-        } else {
-            responseObserver.onNext(GrpcServiceUrlResponse.newBuilder().setUrl(NachrichtEventTestFactory.URL).build());
-        }
-
-        responseObserver.onCompleted();
-    }
 }
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventGrpcTestConfiguration.java b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventGrpcTestConfiguration.java
similarity index 87%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventGrpcTestConfiguration.java
rename to server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventGrpcTestConfiguration.java
index 34f863e2c1d4d489aaf430f96c9a660d948e9039..ec340df8191c4c5380e315eb30f97303b0c65e44 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventGrpcTestConfiguration.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventGrpcTestConfiguration.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -20,10 +17,9 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
+package de.ozgcloud.antragsraum.events;
 
-package de.mgm.bup.ozg.antragsraum.events;
-
-import de.mgm.bup.ozg.antragsraum.infomanager.InformationServiceGrpc;
+import de.ozgcloud.info.InformationServiceGrpc;
 import net.devh.boot.grpc.client.autoconfigure.*;
 import net.devh.boot.grpc.client.inject.GrpcClient;
 import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
@@ -55,7 +51,7 @@ public class NachrichtEventGrpcTestConfiguration {
         return new InformationGrpcServiceStub();
     }
 
-    @GrpcClient("info_manager")
+    @GrpcClient("info-manager")
     InformationServiceGrpc.InformationServiceBlockingStub informationServiceBlockingClient;
 
     @Bean
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcUploadBinaryFileMetaDataTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventMapperTest.java
similarity index 50%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcUploadBinaryFileMetaDataTestFactory.java
rename to server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventMapperTest.java
index adc78c294225c783505f162a43b6498c49fdf51e..da1b4d0fd70f6a773370b04e3b03f23d520dc23e 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/attachments/GrpcUploadBinaryFileMetaDataTestFactory.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventMapperTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,21 +19,28 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
+package de.ozgcloud.antragsraum.events;
 
-package de.mgm.bup.ozg.antragsraum.attachments;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
 
-import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData;
+import static org.assertj.core.api.Assertions.*;
 
-public class GrpcUploadBinaryFileMetaDataTestFactory {
-    public static GrpcUploadBinaryFileMetaData create() {
-        return createBuilder().build();
-    }
+class NachrichtEventMapperTest {
+	@Nested
+	class TestMapFromGrpc {
+		@Test
+		void shouldMap() {
+			var nachrichtEvent = NachrichtEventMapper.fromGrpc(GrpcNachrichtTestFactory.createBuilder().build());
 
-    private static GrpcUploadBinaryFileMetaData.Builder createBuilder() {
-        return GrpcUploadBinaryFileMetaData.newBuilder()
-                .setVorgangId(OzgFileTestFactory.VORGANG_ID)
-                .setContentType(OzgFileTestFactory.CONTENT_TYPE)
-                .setSize(OzgFileTestFactory.FILE_SIZE)
-                .setFileName(OzgFileTestFactory.FILE_NAME);
-    }
-}
+			assertThat(nachrichtEvent).isNotNull();
+		}
+
+		@Test
+		void shouldHavePostfachId() {
+			var nachrichtEvent = NachrichtEventMapper.fromGrpc(GrpcNachrichtTestFactory.createBuilder().build());
+
+			assertThat(nachrichtEvent.postfachId()).isEqualTo(GrpcNachrichtTestFactory.POSTFACH_ID);
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteServiceITCase.java b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteServiceITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b9018036108e73a81000477a7c1813498a53370
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteServiceITCase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.events;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import static org.assertj.core.api.Assertions.*;
+
+@SpringBootTest(properties = {
+		"grpc.server.inProcessName=test",
+		"grpc.server.port=-1",
+		"grpc.client.inProcess.address=in-process:test"
+})
+@SpringJUnitConfig(classes = {NachrichtEventGrpcTestConfiguration.class})
+@DirtiesContext
+@ExtendWith(MockitoExtension.class)
+class NachrichtEventRemoteServiceITCase {
+
+	@Autowired
+	NachrichtEventRemoteService nachrichtEventRemoteService;
+
+	@Nested
+	class TestInformationAvailable {
+
+		@Test
+		@DirtiesContext
+		void shouldLoadNachrichtEvents() {
+			var result = nachrichtEventRemoteService.getNachrichtEventsOfPostfach(InformationGrpcServiceStub.NACHRICHT_EVENT.postfachId());
+
+			assertThat(result).hasSize(1);
+		}
+	}
+
+	@Nested
+	class TestNoInformationAvailable {
+		@Test
+		void shouldLoadEmptyNachrichtEvents() {
+			var result = nachrichtEventRemoteService.getNachrichtEventsOfPostfach(InformationGrpcServiceStub.EMPTY_POSTFACH_ID);
+
+			assertThat(result).isEmpty();
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteServiceTest.java
similarity index 52%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteServiceTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteServiceTest.java
index 215542d7c31f3325fdb340da83e2645d4529af9a..3a6780619cde046d123c5ea5ee26756e5284a62f 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/events/NachrichtEventRemoteServiceTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventRemoteServiceTest.java
@@ -1,8 +1,5 @@
 /*
- * 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
- *
+ * Copyright (c) 2023-2024.
  * Lizenziert unter der EUPL, Version 1.2 oder - sobald
  * diese von der Europäischen Kommission genehmigt wurden -
  * Folgeversionen der EUPL ("Lizenz");
@@ -21,10 +18,18 @@
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
 
-package de.mgm.bup.ozg.antragsraum.events;
+package de.ozgcloud.antragsraum.events;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.when;
 
-import de.mgm.bup.ozg.antragsraum.infomanager.*;
-import de.mgm.bup.ozg.antragsraum.nachricht.NachrichtEventTestFactory;
+import de.ozgcloud.info.GrpcInformationRequest;
+import de.ozgcloud.info.GrpcInformationResponse;
+import de.ozgcloud.info.InformationServiceGrpc;
+import de.ozgcloud.info.nachricht.GrpcNachricht;
+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;
@@ -34,12 +39,6 @@ import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 
-import java.util.List;
-import java.util.UUID;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
 @ExtendWith(MockitoExtension.class)
 class NachrichtEventRemoteServiceTest {
 
@@ -53,12 +52,13 @@ class NachrichtEventRemoteServiceTest {
     class TestInformationAvailable {
 
         private final String postfachId = UUID.randomUUID().toString();
-        private final List<GrpcNachricht> nachrichten = List.of(GrpcNachrichtTestFactory.createBuilder().setPostfachId(postfachId).build());
+        private final List<GrpcNachricht> nachrichten =
+            List.of(GrpcNachrichtTestFactory.createBuilder().setPostfachId(postfachId).build());
 
         @BeforeEach
         void init() {
             when(informationServiceBlockingClient.getInformation(any(GrpcInformationRequest.class)))
-                    .thenReturn(GrpcInformationResponse.newBuilder().addAllNachrichten(nachrichten).build());
+                .thenReturn(GrpcInformationResponse.newBuilder().addAllNachrichten(nachrichten).build());
         }
 
         @Test
@@ -74,20 +74,6 @@ class NachrichtEventRemoteServiceTest {
 
             assertThat(events.get(0).postfachId()).isEqualTo(postfachId);
         }
-
-        @Test
-        void shouldHaveNachrichtenId() {
-            var events = remoteService.getNachrichtEventsOfPostfach(postfachId);
-
-            assertThat(events.get(0).rueckfrageId()).isEqualTo(GrpcNachrichtTestFactory.NACHRICHTEN_ID);
-        }
-
-        @Test
-        void shouldHaveVorgangId() {
-            var events = remoteService.getNachrichtEventsOfPostfach(postfachId);
-
-            assertThat(events.get(0).vorgangId()).isEqualTo(GrpcNachrichtTestFactory.VORGANG_ID);
-        }
     }
 
     @Nested
@@ -98,7 +84,7 @@ class NachrichtEventRemoteServiceTest {
         @BeforeEach
         void init() {
             when(informationServiceBlockingClient.getInformation(any(GrpcInformationRequest.class)))
-                    .thenReturn(GrpcInformationResponse.newBuilder().addAllNachrichten(nachrichten).build());
+                .thenReturn(GrpcInformationResponse.newBuilder().addAllNachrichten(nachrichten).build());
         }
 
         @Test
@@ -108,36 +94,4 @@ class NachrichtEventRemoteServiceTest {
             assertThat(events).hasSize(0);
         }
     }
-
-    @Nested
-    class TestGetRemoteServiceUrl {
-        @BeforeEach
-        void init() {
-            when(informationServiceBlockingClient.getServiceUrlOfNachricht(any(GrpcServiceUrlRequest.class)))
-                    .thenReturn(GrpcServiceUrlResponse.newBuilder().setUrl(NachrichtEventTestFactory.URL).build());
-        }
-
-        @Test
-        void shouldGetUrl() {
-            var url = remoteService.getServiceUrl(NachrichtEventTestFactory.NACHRICHT_ID);
-
-            assertThat(url).hasValue(NachrichtEventTestFactory.URL);
-        }
-    }
-
-    @Nested
-    class TestGetEmptyRemoteServiceUrl {
-        @BeforeEach
-        void init() {
-            when(informationServiceBlockingClient.getServiceUrlOfNachricht(any(GrpcServiceUrlRequest.class)))
-                    .thenReturn(GrpcServiceUrlResponse.getDefaultInstance());
-        }
-
-        @Test
-        void shouldGetUrl() {
-            var url = remoteService.getServiceUrl(NachrichtEventTestFactory.NACHRICHT_ID);
-
-            assertThat(url).isNotPresent();
-        }
-    }
 }
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplicationTest.java b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventServiceTest.java
similarity index 51%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplicationTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventServiceTest.java
index 95e8450191ca1ff99c31ed0b2a78af193c2230f0..c0359770835455fd29f7ec55470df0836bba0fe6 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/AntragsRaumApplicationTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/events/NachrichtEventServiceTest.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,42 +19,30 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
+package de.ozgcloud.antragsraum.events;
 
-package de.mgm.bup.ozg.antragsraum;
-
+import de.ozgcloud.antragsraum.nachricht.NachrichtTestFactory;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
+import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
-import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
 
 @ExtendWith(MockitoExtension.class)
-class AntragsRaumApplicationTest {
-    @Spy
-    @InjectMocks
-    private AntragsRaumApplication application;
-
-    @Test
-    void shouldCreateThreadPoolTaskExecutor() {
-        var threadPool = application.threadPoolTaskExecutor();
-
-        assertThat(threadPool).isNotNull().isInstanceOf(ThreadPoolTaskExecutor.class);
-    }
-
-    @Test
-    void shouldCreateCallScope() {
-        var callScope = application.callScope();
-
-        assertThat(callScope).isNotNull();
-    }
-
-    @Test
-    void shouldCreateBeanFactoryPostProcessor() {
-        var beanProcessor = AntragsRaumApplication.beanFactoryPostProcessor(new CallScope());
-
-        assertThat(beanProcessor).isNotNull();
-    }
+class NachrichtEventServiceTest {
+	@Spy
+	@InjectMocks
+	private NachrichtEventService service;
+	@Mock
+	private NachrichtEventRemoteService remoteService;
+
+	@Test
+	void shouldCallRemoteServiceForEvents() {
+		service.getNachrichtEventsOfPostfachId(NachrichtTestFactory.POSTFACH_ID);
+
+		verify(remoteService).getNachrichtEventsOfPostfach(anyString());
+	}
 }
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/AntragsraumServiceGrpc.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/AntragsraumServiceGrpc.java
new file mode 100644
index 0000000000000000000000000000000000000000..1116d7f96fefe0a327674b227a690b308667e224
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/AntragsraumServiceGrpc.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import de.ozgcloud.nachrichten.antragraum.AntragraumServiceGrpc;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerResponse;
+import io.grpc.stub.StreamObserver;
+import net.devh.boot.grpc.server.service.GrpcService;
+
+@GrpcService
+public class AntragsraumServiceGrpc extends AntragraumServiceGrpc.AntragraumServiceImplBase {
+	@Override
+	public void findRueckfragen(GrpcFindRueckfragenRequest request,
+	  StreamObserver<GrpcFindRueckfragenResponse> responseObserver) {
+		responseObserver.onNext(GrpcRueckfragenTestFactory.createFindRueckfragenResponse());
+		responseObserver.onCompleted();
+	}
+
+	@Override
+	public void sendRueckfrageAnswer(GrpcSendRueckfrageAnswerRequest request,
+	  StreamObserver<GrpcSendRueckfrageAnswerResponse> responseObserver) {
+		responseObserver.onNext(GrpcRueckfragenTestFactory.createSendRueckfrageAnswerResponse());
+		responseObserver.onCompleted();
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/AttachmentMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/AttachmentMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a1efc4633fcbf52c99b344deac883e20bc1f455
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/AttachmentMapperTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.UUID;
+
+import org.apache.http.entity.ContentType;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import de.ozgcloud.vorgang.grpc.binaryFile.GrpcBinaryFile;
+
+class AttachmentMapperTest {
+	static final String ID = UUID.randomUUID().toString();
+	static final String CONTENT_TYPE = ContentType.APPLICATION_OCTET_STREAM.toString();
+	static final long FILE_SIZE = 10;
+	static final String FILE_NAME = "Testfile.zip";
+
+	GrpcBinaryFile file = GrpcBinaryFile.newBuilder()
+	  .setId(ID)
+	  .setName(FILE_NAME)
+	  .setSize(FILE_SIZE)
+	  .setContentType(CONTENT_TYPE)
+	  .build();
+
+	@Nested
+	class MapFromGrpcBinaryFile {
+		@Test
+		void shouldMapAttachment() {
+			var res = AttachmentMapper.fromGrpcBinaryFile(file);
+
+			assertThat(res).isNotNull();
+		}
+
+		@Test
+		void shouldMapAttachmentId() {
+			var res = AttachmentMapper.fromGrpcBinaryFile(file).id();
+
+			assertThat(res).isEqualTo(ID);
+		}
+
+		@Test
+		void shouldMapAttachmentName() {
+			var res = AttachmentMapper.fromGrpcBinaryFile(file).fileName();
+
+			assertThat(res).isEqualTo(FILE_NAME);
+		}
+
+		@Test
+		void shouldMapAttachmentSize() {
+			var res = AttachmentMapper.fromGrpcBinaryFile(file).fileSize();
+
+			assertThat(res).isEqualTo(FILE_SIZE);
+		}
+
+		@Test
+		void shouldMapAttachmentContentType() {
+			var res = AttachmentMapper.fromGrpcBinaryFile(file).contentType();
+
+			assertThat(res).isEqualTo(CONTENT_TYPE);
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/CommandGrpcService.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/CommandGrpcService.java
similarity index 51%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/CommandGrpcService.java
rename to server/src/test/java/de/ozgcloud/antragsraum/nachricht/CommandGrpcService.java
index edb99700905a68a70647b939c67a524e799a2123..9cd3871cf1cce286ce3af714bdb8134f673161d2 100755
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/CommandGrpcService.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/CommandGrpcService.java
@@ -1,47 +1,45 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc;
-import de.itvsh.ozg.pluto.grpc.command.GrpcCommand;
-import de.itvsh.ozg.pluto.grpc.command.GrpcGetCommandRequest;
-import io.grpc.stub.StreamObserver;
-import net.devh.boot.grpc.server.service.GrpcService;
-
-import java.util.UUID;
-
-@GrpcService
-public class CommandGrpcService extends CommandServiceGrpc.CommandServiceImplBase {
-
-    @Override
-    public void getCommand(GrpcGetCommandRequest request, StreamObserver<GrpcCommand> observer) {
-        if (GrpcCommandTestFactory.ID.equals(request.getId())) {
-            observer.onNext(GrpcCommandTestFactory.createBuilder().setStatus(GrpcCommandTestFactory.STATUS_FINISHED).build());
-        } else {
-            observer.onNext(GrpcCommandTestFactory.createBuilder()
-                    .setStatus(GrpcCommandTestFactory.STATUS_PENDING).setId(UUID.randomUUID().toString()).build());
-        }
-        observer.onCompleted();
-    }
-}
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.UUID;
+
+import de.ozgcloud.vorgang.grpc.command.CommandServiceGrpc;
+import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
+import de.ozgcloud.vorgang.grpc.command.GrpcGetCommandRequest;
+import io.grpc.stub.StreamObserver;
+import net.devh.boot.grpc.server.service.GrpcService;
+
+@GrpcService
+public class CommandGrpcService extends CommandServiceGrpc.CommandServiceImplBase {
+
+	@Override
+	public void getCommand(GrpcGetCommandRequest request, StreamObserver<GrpcCommand> observer) {
+		if (GrpcCommandTestFactory.ID.equals(request.getId())) {
+			observer.onNext(GrpcCommandTestFactory.createBuilder().setStatus(GrpcCommandTestFactory.STATUS_FINISHED).build());
+		} else {
+			observer.onNext(GrpcCommandTestFactory.createBuilder()
+			  .setStatus(GrpcCommandTestFactory.STATUS_PENDING).setId(UUID.randomUUID().toString()).build());
+		}
+		observer.onCompleted();
+	}
+}
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcCommandTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcCommandTestFactory.java
similarity index 57%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcCommandTestFactory.java
rename to server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcCommandTestFactory.java
index ee01f71dea240cfdb38dbe1a2917a14ea8db0842..6dbef35d07242d2b47b88908cbfa04f4b8ce02ab 100755
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/GrpcCommandTestFactory.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcCommandTestFactory.java
@@ -1,46 +1,44 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import de.itvsh.ozg.pluto.grpc.command.GrpcCommand;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-
-import java.util.UUID;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-class GrpcCommandTestFactory {
-    static final String ID = UUID.randomUUID().toString();
-    static final String STATUS_SEND = "SEND";
-    static final String STATUS_FINISHED = "FINISHED";
-    static final String STATUS_PENDING = "PENDING";
-
-    static GrpcCommand create() {
-        return createBuilder().build();
-    }
-
-    static GrpcCommand.Builder createBuilder() {
-        return GrpcCommand.newBuilder().setStatus(STATUS_SEND).setId(ID);
-    }
-}
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.UUID;
+
+import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+class GrpcCommandTestFactory {
+	static final String ID = UUID.randomUUID().toString();
+	static final String STATUS_SEND = "SEND";
+	static final String STATUS_FINISHED = "FINISHED";
+	static final String STATUS_PENDING = "PENDING";
+
+	static GrpcCommand create() {
+		return createBuilder().build();
+	}
+
+	static GrpcCommand.Builder createBuilder() {
+		return GrpcCommand.newBuilder().setStatus(STATUS_SEND).setId(ID);
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcPostfachMailTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcPostfachMailTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5aafce38bcee6f1564127a280b9ef916637f25b
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcPostfachMailTestFactory.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.UUID;
+
+import de.ozgcloud.nachrichten.postfach.GrpcDirection;
+import de.ozgcloud.nachrichten.postfach.GrpcPostfachAddress;
+import de.ozgcloud.nachrichten.postfach.GrpcPostfachMail;
+import de.ozgcloud.vorgang.common.GrpcObject;
+import de.ozgcloud.vorgang.common.GrpcProperty;
+
+public class GrpcPostfachMailTestFactory {
+	static final String NACHRICHT_ID = UUID.randomUUID().toString();
+	static final String VORGANG_ID = NachrichtTestFactory.VORGANG_ID;
+
+	static final String POSTFACH_ID_FIELD = "postfachId";
+
+	static GrpcPostfachMail create() {
+		return createBuilder().build();
+	}
+
+	static GrpcPostfachMail.Builder createBuilder() {
+		return GrpcPostfachMail.newBuilder()
+		  .setId(NACHRICHT_ID)
+		  .setVorgangId(VORGANG_ID)
+		  .setPostfachAddress(createPostfachAddress())
+		  .setMailBody(NachrichtTestFactory.TEXT)
+		  .setSubject(NachrichtTestFactory.VORGANG_TITLE)
+		  .setDirection(GrpcDirection.OUT)
+		  .setSentAt(NachrichtTestFactory.DATE_STRING)
+		  .setCreatedAt(NachrichtTestFactory.DATE_STRING)
+		  .setReplyOption(NachrichtTestFactory.REPLY_FORBIDDEN.name())
+		  .addAttachment(NachrichtTestFactory.ATTACHMENT_ID_1)
+		  .addAttachment(NachrichtTestFactory.ATTACHMENT_ID_2);
+	}
+
+	private static GrpcPostfachAddress createPostfachAddress() {
+		return GrpcPostfachAddress.newBuilder()
+		  .setIdentifier(GrpcObject.newBuilder()
+			.addProperty(
+			  GrpcProperty.newBuilder()
+				.setName(POSTFACH_ID_FIELD)
+				.addValue(NachrichtTestFactory.POSTFACH_ID).build()
+			).build()
+		  ).build();
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcRueckfragenTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcRueckfragenTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..6307efa21587ae39fb6bbf4389988d70138ef9df
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcRueckfragenTestFactory.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.UUID;
+
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrage;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrageAnswer;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerResponse;
+
+public class GrpcRueckfragenTestFactory {
+	static final String RUECKFRAGE_ID = UUID.randomUUID().toString();
+	static final String VORGANG_ID = UUID.randomUUID().toString();
+	static final String VORGANG_NAME = NachrichtTestFactory.VORGANG_TITLE;
+	static final String ANSWER_FILE_ID = UUID.randomUUID().toString();
+
+	public static final String TEXT = """
+	  Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
+	   sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
+	   sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
+	   
+	   Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+	   Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
+	  """;
+
+	public static final String ANSWER_TEXT = """
+	  ANTWORT Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+	   Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
+	  """;
+
+	public static final String SEND_AT = "2022-10-26T09:26:25";
+
+	public static final long SEND_AT_DATE = LocalDateTime.parse(SEND_AT).toEpochSecond(ZoneOffset.UTC);
+
+	public static final String ANSWERED_AT = "2022-10-27T12:00:00";
+
+	public static final long ANSWERED_AT_DATE = LocalDateTime.parse(ANSWERED_AT).toEpochSecond(ZoneOffset.UTC);
+
+	static GrpcFindRueckfragenResponse createFindRueckfragenResponse() {
+		return GrpcFindRueckfragenResponse.newBuilder().addRueckfrage(
+			createRueckfrageBuilder()
+			  .build())
+		  .build();
+	}
+
+	static GrpcSendRueckfrageAnswerResponse createSendRueckfrageAnswerResponse() {
+		return GrpcSendRueckfrageAnswerResponse.newBuilder().setCommandId(GrpcCommandTestFactory.ID).build();
+	}
+
+	static GrpcRueckfrage create() {
+		return createRueckfrageBuilder().build();
+	}
+
+	static GrpcRueckfrage createWithReply() {
+		return createRueckfrageBuilder()
+		  .setAnsweredAt(ANSWERED_AT)
+		  .addAnswers(GrpcRueckfrageAnswer.newBuilder()
+			.setAnswerText(ANSWER_TEXT)
+			.addAttachmentFileId(ANSWER_FILE_ID)
+			.build())
+		  .build();
+	}
+
+	static GrpcRueckfrage.Builder createRueckfrageBuilder() {
+		return GrpcRueckfrage.newBuilder()
+		  .setId(RUECKFRAGE_ID)
+		  .setVorgangId(VORGANG_ID)
+		  .setVorgangName(VORGANG_NAME)
+		  .setText(TEXT)
+		  .setSentAt(SEND_AT)
+		  .addAllAttachmentFileId(
+			List.of(NachrichtTestFactory.ATTACHMENT_ID_1, NachrichtTestFactory.ATTACHMENT_ID_2));
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcServerInterceptor.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcServerInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..470732826c41e30b2eb8e6e9314d967260bdceed
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/GrpcServerInterceptor.java
@@ -0,0 +1,39 @@
+/*
+ * 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.antragsraum.nachricht;
+
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import lombok.Getter;
+
+@Getter
+public class GrpcServerInterceptor implements ServerInterceptor {
+	private Metadata metadata;
+
+	@Override
+	public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata,
+	  ServerCallHandler<ReqT, RespT> serverCallHandler) {
+		this.metadata = metadata;
+		return serverCallHandler.startCall(serverCall, metadata);
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtEventTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtEventTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfb16e86a43cb138145d78f04fd0b016dfce33d8
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtEventTestFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.util.UUID;
+
+import de.ozgcloud.antragsraum.events.NachrichtEvent;
+
+public class NachrichtEventTestFactory {
+	public static final String POSTFACH_ID = UUID.randomUUID().toString();
+	public static final String VORGANG_ID = UUID.randomUUID().toString();
+	public static final String URL = "http://localhost";
+
+	public static NachrichtEvent create() {
+		return createBuilder().build();
+	}
+
+	public static NachrichtEvent.NachrichtEventBuilder createBuilder() {
+		return NachrichtEvent.builder()
+		  .postfachId(POSTFACH_ID)
+		  .address(URL);
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeaderMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeaderMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d4aa86fb84f58183080bdb51c72f9182d97d1b6
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtHeaderMapperTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class NachrichtHeaderMapperTest {
+	@Nested
+	class TestFromNachricht {
+		private final Nachricht nachricht = NachrichtTestFactory.createNachricht();
+
+		@Test
+		void shouldMapNachrichtId() {
+			var nachrichtHeader = NachrichtHeaderMapper.fromNachricht(nachricht);
+
+			assertThat(nachrichtHeader.id()).isEqualTo(NachrichtTestFactory.ID);
+		}
+
+		@Test
+		void shouldMapAddress() {
+			var nachrichtHeader = NachrichtHeaderMapper.fromNachricht(nachricht);
+
+			assertThat(nachrichtHeader.address()).isEqualTo(NachrichtTestFactory.ADDRESS);
+		}
+
+		@Test
+		void shouldMapVorgangId() {
+			var nachrichtHeader = NachrichtHeaderMapper.fromNachricht(nachricht);
+
+			assertThat(nachrichtHeader.vorgangId()).isEqualTo(NachrichtTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldMapVorgangName() {
+			var nachrichtHeader = NachrichtHeaderMapper.fromNachricht(nachricht);
+
+			assertThat(nachrichtHeader.topicTitle()).isEqualTo(NachrichtTestFactory.VORGANG_TITLE);
+		}
+
+		@Test
+		void shouldMapCreationDate() {
+			var nachrichtHeader = NachrichtHeaderMapper.fromNachricht(nachricht);
+
+			assertThat(nachrichtHeader.date()).isEqualTo(NachrichtTestFactory.DATE);
+		}
+
+		@Test
+		void shouldMapLastAnsweredDate() {
+			var nachrichtHeader = NachrichtHeaderMapper.fromNachricht(nachricht);
+
+			assertThat(nachrichtHeader.lastAnsweredDate()).isEqualTo(NachrichtTestFactory.DATE);
+		}
+
+		@Test
+		void shouldMapTrustLevel() {
+			var nachrichtHeader = NachrichtHeaderMapper.fromNachricht(nachricht);
+
+			assertThat(nachrichtHeader.trustLevel()).isEqualTo(NachrichtTrustLevel.LOW);
+		}
+	}
+
+	@Nested
+	class TestGetLatestReplyDate {
+		@Test
+		void shouldHaveNoDate() {
+			var nachricht = Nachricht.builder().replyNachrichten(Collections.emptyList()).build();
+			var result = NachrichtHeaderMapper.getLatestReplyDate(nachricht);
+
+			assertThat(result).isNull();
+		}
+
+		@Test
+		void shouldReturnLatestDate() {
+			var replyNachrichten = List.of(
+			  ReplyNachricht.builder().date(NachrichtTestFactory.DATE).build(),
+			  ReplyNachricht.builder().date(NachrichtTestFactory.DATE - 1000).build()
+			);
+			var nachricht = Nachricht.builder().replyNachrichten(replyNachrichten).build();
+			var result = NachrichtHeaderMapper.getLatestReplyDate(nachricht);
+
+			assertThat(result).isEqualTo(NachrichtTestFactory.DATE);
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6dd00b933dec2c1890134eadf2537b45ad351d27
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtMapperTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+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.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import de.ozgcloud.antragsraum.attachments.FileRemoteService;
+import de.ozgcloud.antragsraum.attachments.OzgFile;
+import de.ozgcloud.antragsraum.attachments.OzgFileTestFactory;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrage;
+
+@ExtendWith(MockitoExtension.class)
+class NachrichtMapperTest {
+	@Mock
+	private FileRemoteService fileRemoteService;
+
+	@InjectMocks
+	private NachrichtMapper mapper;
+
+	@Nested
+	class TestFromGrpcRueckfrage {
+		private final GrpcRueckfrage rueckfrage = GrpcRueckfragenTestFactory.create();
+		private final GrpcRueckfrage answeredRueckfrage = GrpcRueckfragenTestFactory.createWithReply();
+
+		@BeforeEach
+		void setup() {
+			when(fileRemoteService.getFile(anyString(), anyString())).thenReturn(OzgFileTestFactory.createOzgFile());
+		}
+
+		@Test
+		void shouldMapNachrichtId() {
+			var nachricht = mapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.createRueckfrageBuilder().setId(NachrichtTestFactory.ID).build(),
+			  NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.id()).isEqualTo(NachrichtTestFactory.ID);
+		}
+
+		@Test
+		void shouldMapAddress() {
+			var nachricht = mapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.createRueckfrageBuilder().setId(NachrichtTestFactory.ID).build(),
+			  NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.address()).isEqualTo(NachrichtTestFactory.ADDRESS);
+		}
+
+		@Test
+		void shouldMapVorgangId() {
+			var nachricht = mapper.fromGrpcRueckfrage(rueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.vorgangId()).isEqualTo(GrpcRueckfragenTestFactory.VORGANG_ID);
+		}
+
+		@Test
+		void shouldMapVorgangName() {
+			var nachricht = mapper.fromGrpcRueckfrage(rueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.topicTitle()).isEqualTo(NachrichtTestFactory.VORGANG_TITLE);
+		}
+
+		@Test
+		void shouldMapNachrichtText() {
+			var nachricht = mapper.fromGrpcRueckfrage(rueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.message()).isEqualTo(GrpcRueckfragenTestFactory.TEXT);
+		}
+
+		@Test
+		void shouldMapCreationDate() {
+			var nachricht = mapper.fromGrpcRueckfrage(rueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.date()).isEqualTo(GrpcRueckfragenTestFactory.SEND_AT_DATE);
+		}
+
+		@Test
+		void shouldMapInvalidCreationDate() {
+			var nachricht = mapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.createRueckfrageBuilder().setSentAt("huu").build(),
+			  NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.date()).isZero();
+		}
+
+		@Test
+		void shouldMapAttachments() {
+			var nachricht = mapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.create(), NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.attachments()).hasSize(2);
+		}
+
+		@Test
+		void shouldMapEmptyAttachments() {
+			when(fileRemoteService.getFile(anyString(), anyString())).thenReturn(null);
+
+			var nachricht = mapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.create(), NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.attachments()).isEmpty();
+		}
+
+		@Test
+		void shouldMapTrustLevel() {
+			var nachricht = mapper.fromGrpcRueckfrage(answeredRueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.trustLevel()).isEqualTo(NachrichtTrustLevel.LOW);
+		}
+
+		@Test
+		void shouldNotHaveAnswersWhenUnanswered() {
+			var nachricht = mapper.fromGrpcRueckfrage(GrpcRueckfragenTestFactory.create(), NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.replyNachrichten()).isEmpty();
+		}
+
+		@Test
+		void shouldMapAnswerNachrichtText() {
+			var nachricht = mapper.fromGrpcRueckfrage(answeredRueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.replyNachrichten().getFirst().message()).isEqualTo(GrpcRueckfragenTestFactory.ANSWER_TEXT);
+		}
+
+		@Test
+		void shouldMapAnsweredDate() {
+			var nachricht = mapper.fromGrpcRueckfrage(answeredRueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.replyNachrichten().getFirst().date()).isEqualTo(GrpcRueckfragenTestFactory.ANSWERED_AT_DATE);
+		}
+
+		@Test
+		void shouldMapAnswerAttachments() {
+			var nachricht = mapper.fromGrpcRueckfrage(answeredRueckfrage, NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.replyNachrichten().getFirst().attachments()).hasSize(1);
+		}
+	}
+
+	@Nested
+	class TestToGrpcRueckfrageAnswer {
+		@Test
+		void shouldMapNachrichtText() {
+			var nachricht = mapper.toGrpcRueckfrageAnswer(ReplyNachrichtTestFactory.create());
+
+			assertThat(nachricht.getAnswerText()).isEqualTo(ReplyNachrichtTestFactory.TEXT);
+		}
+
+		@Test
+		void shouldMapEmptyAttachments() {
+			var nachricht = mapper.toGrpcRueckfrageAnswer(ReplyNachrichtTestFactory.create());
+
+			assertThat(nachricht.getAttachmentFileIdList()).isEmpty();
+		}
+
+		@Test
+		void shouldMapAttachments() {
+			var nachricht = mapper.toGrpcRueckfrageAnswer(
+			  ReplyNachrichtTestFactory.createBuilder()
+				.attachments(List.of(OzgFile.builder().id(UUID.randomUUID().toString()).build())).build());
+
+			assertThat(nachricht.getAttachmentFileIdList()).hasSize(1);
+		}
+
+		@Test
+		void shouldNotMapEmptyAttachments() {
+			var nachricht = mapper.fromGrpcRueckfrage(
+			  GrpcRueckfragenTestFactory.createRueckfrageBuilder().clearAttachmentFileId().addAttachmentFileId("").build(),
+			  NachrichtTestFactory.ADDRESS);
+
+			assertThat(nachricht.attachments()).isEmpty();
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtPropertiesTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtPropertiesTest.java
similarity index 56%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtPropertiesTest.java
rename to server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtPropertiesTest.java
index dfb63ef666939335baaa492a22ce158edcccac4d..626f42274e2ed86e9b93e721d7b027447a39d7b6 100755
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtPropertiesTest.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtPropertiesTest.java
@@ -1,52 +1,50 @@
-/*
- * 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.mgm.bup.ozg.antragsraum.nachricht;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.time.Duration;
-import java.time.temporal.ChronoUnit;
-
-import static org.assertj.core.api.Assertions.*;
-
-class NachrichtPropertiesTest {
-    private final NachrichtProperties properties = new NachrichtProperties();
-
-    @BeforeEach
-    void setup() {
-        properties.setSendTimeout("PT120S");
-        properties.setSendPollInterval("PT300S");
-    }
-
-    @Test
-    void shouldGetSendTimeoutDuration() {
-        assertThat(properties.getTimeoutDuration()).isEqualTo(Duration.of(120, ChronoUnit.SECONDS));
-    }
-
-    @Test
-    void shouldGetSendPollIntervallDuration() {
-        assertThat(properties.getSendPollIntervalDuration()).isEqualTo(Duration.of(300, ChronoUnit.SECONDS));
-    }
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class NachrichtPropertiesTest {
+	private final NachrichtProperties properties = new NachrichtProperties();
+
+	@BeforeEach
+	void setup() {
+		properties.setSendTimeout("PT120S");
+		properties.setSendPollInterval("PT300S");
+	}
+
+	@Test
+	void shouldGetSendTimeoutDuration() {
+		assertThat(properties.getTimeoutDuration()).isEqualTo(Duration.of(120, ChronoUnit.SECONDS));
+	}
+
+	@Test
+	void shouldGetSendPollIntervallDuration() {
+		assertThat(properties.getSendPollIntervalDuration()).isEqualTo(Duration.of(300, ChronoUnit.SECONDS));
+	}
 }
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..eeae99b8f93b5cbb51eb9223e0d7d11591d3f96a
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtTestFactory.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+public class NachrichtTestFactory {
+	public static final String ID = UUID.randomUUID().toString();
+	public static final String POSTFACH_ID = UUID.randomUUID().toString();
+	public static final String VORGANG_ID = UUID.randomUUID().toString();
+	public static final String TEXT = """
+	  Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
+	   sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
+	   sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
+	   
+	   Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+	   Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
+	  """;
+	public static final String VORGANG_TITLE = "Vorgang Test ";
+	public static final String ATTACHMENT_ID_1 = "6358fd3f46811d04010f44c8";
+	public static final String ATTACHMENT_ID_2 = "6358fd4146811d04010f44d0";
+	public static final String DATE_STRING = "2022-10-26T09:26:25";
+	public static final long DATE = LocalDateTime.parse(DATE_STRING).toEpochSecond(ZoneOffset.UTC);
+	public static final ReplyOption REPLY_FORBIDDEN = ReplyOption.FORBIDDEN;
+	public static final ReplyOption REPLY_ALLOWED = ReplyOption.ALLOWED;
+	public static final String REPLY_TO_NACHRICHT_ID = UUID.randomUUID().toString();
+	public static final String ADDRESS = "static://localhost:9090";
+
+	static Nachricht createNachricht() {
+		return createNachrichtBuilder().build();
+	}
+
+	static Nachricht.NachrichtBuilder createNachrichtBuilder() {
+		return Nachricht.builder()
+		  .id(ID)
+		  .address(ADDRESS)
+		  .message(TEXT)
+		  .topicTitle(VORGANG_TITLE)
+		  .vorgangId(VORGANG_ID)
+		  .postfachId(POSTFACH_ID)
+		  .replyOption(REPLY_ALLOWED)
+		  .replyNachrichten(List.of(ReplyNachricht.builder().address(ADDRESS).date(DATE).build()))
+		  .date(DATE)
+		  .trustLevel(NachrichtTrustLevel.LOW);
+	}
+
+	static NachrichtHeader createNachrichtHeader() {
+		return createNachrichtHeaderBuilder().build();
+	}
+
+	static NachrichtHeader.NachrichtHeaderBuilder createNachrichtHeaderBuilder() {
+		return NachrichtHeader.builder()
+		  .id(ID)
+		  .topicTitle(VORGANG_TITLE)
+		  .vorgangId(VORGANG_ID)
+		  .postfachId(POSTFACH_ID)
+		  .replyOption(REPLY_ALLOWED)
+		  .date(DATE)
+		  .trustLevel(NachrichtTrustLevel.LOW);
+	}
+
+	static Topic createTopic() {
+		return createTopicBuilder().build();
+	}
+
+	static Topic.TopicBuilder createTopicBuilder() {
+		return Topic.builder()
+		  .topicTitle(VORGANG_TITLE)
+		  .clerkMessage(createNachricht())
+		  .trustLevel(NachrichtTrustLevel.LOW)
+		  .userMessages(Collections.emptyList());
+	}
+
+	static TopicHeader createTopicHeader() {
+		return createTopicHeaderBuilder().build();
+	}
+
+	static TopicHeader.TopicHeaderBuilder createTopicHeaderBuilder() {
+		return TopicHeader.builder()
+		  .topicTitle(VORGANG_TITLE)
+		  .clerkMessage(createNachrichtHeader())
+		  .trustLevel(NachrichtTrustLevel.LOW);
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerITCase.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..56f33e94243866e42fd88abfda1cf9884b8dec51
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerITCase.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static de.ozgcloud.antragsraum.nachricht.NachrichtenService.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import de.ozgcloud.antragsraum.attachments.FileRemoteService;
+import de.ozgcloud.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.common.AddressNotFoundException;
+import de.ozgcloud.antragsraum.common.SendTimeoutException;
+import de.ozgcloud.antragsraum.common.TechnicalException;
+
+@AutoConfigureMockMvc
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
+@SpringJUnitConfig(classes = { NachrichtenControllerTestConfiguration.class })
+public class NachrichtenControllerITCase {
+	@SpyBean
+	private NachrichtenController nachrichtenController;
+
+	@Autowired
+	private MockMvc mockMvc;
+
+	@MockBean
+	private NachrichtenService nachrichtenService;
+
+	@MockBean
+	private ContextService contextService;
+
+	@MockBean
+	private FileRemoteService fileRemoteService;
+
+	@Nested
+	class TestLoadingTopicHeaders {
+		@Test
+		@WithMockUser
+		void shouldLoadRueckfragenHeader() throws Exception {
+			when(nachrichtenService.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID)).thenReturn(
+			  List.of(NachrichtTestFactory.createTopicHeader()));
+
+			performRequest()
+			  .andExpect(status().isOk())
+			  .andExpect(jsonPath("$[0]['clerkMessage']['id']").value(NachrichtTestFactory.ID));
+		}
+
+		@Test
+		void shouldNotLoadRueckfragenHeaderUnauthorized() throws Exception {
+			performRequest().andExpect(status().isUnauthorized());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldOnlyIncludeSelfLink() throws Exception {
+			var topicHeaderWithSelfLink = NachrichtTestFactory.createTopicHeader();
+			topicHeaderWithSelfLink.add(linkTo(NachrichtenController.class).withSelfRel());
+
+			when(nachrichtenService.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID)).thenReturn(List.of(topicHeaderWithSelfLink));
+
+			performRequest()
+			  .andExpect(status().isOk())
+			  .andExpect(jsonPath("$[0]['links'].length()").value(1))
+			  .andExpect(jsonPath("$[0]['links'][0]['rel']").value("self"))
+			  .andExpect(jsonPath("$[0]['links'][0]['href']").isString());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldIncludeSelfAndTopicLink() throws Exception {
+			var fullyLinkedTopicHeader = NachrichtTestFactory.createTopicHeader();
+			fullyLinkedTopicHeader.add(linkTo(NachrichtenController.class).withSelfRel());
+			fullyLinkedTopicHeader.add(linkTo(methodOn(NachrichtenController.class).getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID,
+			  fullyLinkedTopicHeader.getClerkMessage().id())).withRel(TOPIC_LINK_RELATIONSHIP_NAME));
+
+			when(nachrichtenService.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID)).thenReturn(List.of(fullyLinkedTopicHeader));
+
+			performRequest()
+			  .andExpect(status().isOk())
+			  .andExpect(jsonPath("$[0]['links'].length()").value(2))
+			  .andExpect(jsonPath("$[0]['links'][0]['rel']").value("self"))
+			  .andExpect(jsonPath("$[0]['links'][0]['href']").isString())
+			  .andExpect(jsonPath("$[0]['links'][1]['rel']").value(TOPIC_LINK_RELATIONSHIP_NAME))
+			  .andExpect(jsonPath("$[0]['links'][1]['href']").isString());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldHandleRuntimeExceptions() throws Exception {
+			doThrow(RuntimeException.class).when(nachrichtenService).getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			performRequest().andExpect(status().isInternalServerError());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldHandleTechnicalException() throws Exception {
+			doThrow(TechnicalException.class).when(nachrichtenService).getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			performRequest().andExpect(status().isInternalServerError());
+		}
+
+		ResultActions performRequest() throws Exception {
+			return mockMvc.perform(
+			  get(NachrichtenController.PATH + "/nachrichten/" + NachrichtTestFactory.POSTFACH_ID)
+				.contentType(MediaType.APPLICATION_JSON).characterEncoding(Charset.defaultCharset()));
+		}
+	}
+
+	@Nested
+	class TestLoadingTopic {
+		@BeforeEach
+		void init() {
+			when(nachrichtenService.getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID)).thenReturn(
+			  NachrichtTestFactory.createTopic());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldLoadRueckfrage() throws Exception {
+			performRequest()
+			  .andExpect(status().isOk())
+			  .andExpect(jsonPath("$['clerkMessage']['id']").value(NachrichtTestFactory.ID));
+		}
+
+		@Test
+		void shouldNotLoadRueckfrageUnauthorized() throws Exception {
+			performRequest().andExpect(status().isUnauthorized());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldHandleRuntimeExceptions() throws Exception {
+			doThrow(RuntimeException.class).when(nachrichtenService).getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID);
+
+			performRequest().andExpect(status().isInternalServerError());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldHandleTechnicalException() throws Exception {
+			doThrow(TechnicalException.class).when(nachrichtenService).getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID);
+
+			performRequest().andExpect(status().isInternalServerError());
+		}
+
+		ResultActions performRequest() throws Exception {
+			return mockMvc.perform(
+			  get(NachrichtenController.PATH + "/nachrichten/" + NachrichtTestFactory.POSTFACH_ID + "/" + NachrichtTestFactory.ID)
+				.contentType(MediaType.APPLICATION_JSON).characterEncoding(Charset.defaultCharset()));
+		}
+	}
+
+	@Nested
+	class TestSendingNachricht {
+		private final ObjectMapper mapper = new ObjectMapper();
+		private final ReplyNachricht msg = ReplyNachrichtTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			doReturn(msg).when(nachrichtenService).sendRueckfrageAnswer(any(ReplyNachricht.class));
+		}
+
+		@Test
+		@WithMockUser
+		void shouldSendNachricht() throws Exception {
+			performPutRequest(mapper.writeValueAsString(msg))
+			  .andExpect(status().isOk())
+			  .andExpect(jsonPath("$.id").value(ReplyNachrichtTestFactory.ID));
+		}
+
+		@Test
+		@WithMockUser
+		void shouldNotSendNachrichtBlankId() throws Exception {
+			var invalidMsg = ReplyNachrichtTestFactory.createBuilder().id("").build();
+
+			performPutRequest(mapper.writeValueAsString(invalidMsg))
+			  .andExpect(status().isBadRequest());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldNotSendNachrichtNoAddress() throws Exception {
+			doThrow(AddressNotFoundException.class).when(nachrichtenService).sendRueckfrageAnswer(any());
+
+			performPutRequest(mapper.writeValueAsString(msg))
+			  .andExpect(status().isNotFound());
+		}
+
+		@Test
+		@WithMockUser
+		void shouldNotSendNachrichtTimeout() throws Exception {
+			doThrow(new SendTimeoutException("test", new TimeoutException())).when(nachrichtenService).sendRueckfrageAnswer(any());
+
+			performPutRequest(mapper.writeValueAsString(msg))
+			  .andExpect(status().isGatewayTimeout());
+		}
+
+		ResultActions performPutRequest(String body) throws Exception {
+			return mockMvc.perform(
+			  put(NachrichtenController.PATH + "/nachricht")
+				.with(csrf().asHeader())
+				.contentType(MediaType.APPLICATION_JSON)
+				.characterEncoding(Charset.defaultCharset())
+				.content(body));
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..51a64509712e8c43d8cdbd537741de4792b37641
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+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.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class NachrichtenControllerTest {
+	@Spy
+	@InjectMocks
+	private NachrichtenController nachrichtenController;
+
+	@Mock
+	private NachrichtenService nachrichtenService;
+
+	@Nested
+	class TestLoadingTopicHeaders {
+		@BeforeEach
+		void init() {
+			when(nachrichtenService.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID)).thenReturn(
+			  List.of(NachrichtTestFactory.createTopicHeader()));
+		}
+
+		@Test
+		void shouldLoadTopicHeaders() {
+			var result = nachrichtenController.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			assertThat(result).hasSize(1);
+		}
+
+		@Test
+		void shouldCallGetTopicHeadersOfPostfach() {
+			nachrichtenController.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			verify(nachrichtenService).getTopicHeadersOfPostfach(anyString());
+		}
+	}
+
+	@Nested
+	class TestLoadingTopic {
+		@BeforeEach
+		void init() {
+			when(nachrichtenService.getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID)).thenReturn(
+			  NachrichtTestFactory.createTopic());
+		}
+
+		@Test
+		void shouldLoadTopic() {
+			var result = nachrichtenController.getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID);
+
+			assertThat(result).isNotNull();
+		}
+
+		@Test
+		void shouldCallGetTopicOfPostfach() {
+			nachrichtenController.getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID);
+
+			verify(nachrichtenService).getTopicOfPostfach(anyString(), anyString());
+		}
+	}
+
+	@Nested
+	class TestSendingAnswer {
+		@Test
+		void shouldCallService() {
+			nachrichtenController.sendNachricht(ReplyNachrichtTestFactory.create());
+
+			verify(nachrichtenService).sendRueckfrageAnswer(any(ReplyNachricht.class));
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerTestConfiguration.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerTestConfiguration.java
similarity index 65%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerTestConfiguration.java
rename to server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerTestConfiguration.java
index 20b7da2d010294e3def492cb2c03e503e7366f5e..a37b2a86e922131c72b148a094d73aeee4a918c8 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/NachrichtenControllerTestConfiguration.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenControllerTestConfiguration.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,11 +19,8 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
+package de.ozgcloud.antragsraum.nachricht;
 
-package de.mgm.bup.ozg.antragsraum.nachricht;
-
-import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
-import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
@@ -33,20 +29,23 @@ import org.springframework.context.annotation.FilterType;
 import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
+import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
+import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+
 @EnableWebMvc
 @EnableAsync
-@ComponentScan(value = {"de.mgm.bup.ozg.antragsraum.nachricht", "de.mgm.bup.ozg.antragsraum.common"},
-        excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
-                classes = NachrichtenServiceGrpcTestConfiguration.class))
+@ComponentScan(value = { "de.ozgcloud.antragsraum.nachricht", "de.ozgcloud.antragsraum.common" },
+  excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
+	classes = NachrichtenServiceGrpcTestConfiguration.class))
 @Configuration
 public class NachrichtenControllerTestConfiguration {
-    @Bean
-    GrpcChannelsProperties grpcChannelsProperties() {
-        return new GrpcChannelsProperties();
-    }
+	@Bean
+	GrpcChannelsProperties grpcChannelsProperties() {
+		return new GrpcChannelsProperties();
+	}
 
-    @Bean
-    GlobalClientInterceptorRegistry globalClientInterceptorRegistry(final ApplicationContext applicationContext) {
-        return new GlobalClientInterceptorRegistry(applicationContext);
-    }
+	@Bean
+	GlobalClientInterceptorRegistry globalClientInterceptorRegistry(final ApplicationContext applicationContext) {
+		return new GlobalClientInterceptorRegistry(applicationContext);
+	}
 }
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenGrpcClientTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenGrpcClientTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb93d827b561d34739b17c801b838aacd9292132
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenGrpcClientTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.UUID;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import com.google.common.collect.ImmutableList;
+
+import de.ozgcloud.antragsraum.common.ChannelPropertiesFactory;
+import de.ozgcloud.nachrichten.antragraum.AntragraumServiceGrpc;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerResponse;
+import de.ozgcloud.vorgang.grpc.command.CommandServiceGrpc;
+import de.ozgcloud.vorgang.grpc.command.GrpcCommand;
+import de.ozgcloud.vorgang.grpc.command.GrpcGetCommandRequest;
+import io.grpc.Channel;
+import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
+import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
+import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+
+@ExtendWith(MockitoExtension.class)
+class NachrichtenGrpcClientTest {
+	private final static String CHANNEL_ADDRESS = "static://localhost:9090";
+	@Spy
+	@InjectMocks
+	private NachrichtenGrpcClient grpcClient;
+
+	@Mock
+	private GlobalClientInterceptorRegistry registry;
+
+	@Mock
+	private ChannelPropertiesFactory channelPropertiesFactory;
+
+	@Nested
+	class TestCreatingChannel {
+		@BeforeEach
+		void init() {
+			when(channelPropertiesFactory.getGrpcChannelsProperties(anyString())).thenReturn(new GrpcChannelsProperties());
+		}
+
+		@Test
+		void shouldGetChannelFactory() {
+			var channelFactory = grpcClient.getChannelFactory(CHANNEL_ADDRESS);
+
+			assertThat(channelFactory).isNotNull();
+		}
+
+		@Test
+		void shouldGetChannel() {
+			when(registry.getClientInterceptors()).thenReturn(ImmutableList.of());
+
+			var channel = grpcClient.getChannelFactory(CHANNEL_ADDRESS).createChannel(CHANNEL_ADDRESS);
+
+			assertThat(channel).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestSendingRequests {
+		@Mock
+		private AntragraumServiceGrpc.AntragraumServiceBlockingStub antragraumServiceBlockingStub;
+
+		@Nested
+		class TestRueckfrageAnswer {
+			@Mock
+			private GrpcSendRueckfrageAnswerRequest request;
+
+			@Test
+			void shouldGetAnswerResponse() {
+				var channelFactory = mock(GrpcChannelFactory.class);
+				var channel = mock(Channel.class);
+				when(channelFactory.createChannel(anyString())).thenReturn(channel);
+				doReturn(channelFactory).when(grpcClient).getChannelFactory(anyString());
+				doReturn(GrpcSendRueckfrageAnswerResponse.getDefaultInstance()).when(grpcClient)
+				  .getAnswerResponse(any(GrpcSendRueckfrageAnswerRequest.class), any());
+
+				grpcClient.answerRueckfrage(request, CHANNEL_ADDRESS);
+
+				verify(grpcClient).getAnswerResponse(any(GrpcSendRueckfrageAnswerRequest.class), any());
+			}
+
+			@Test
+			void shouldSendRueckfrageAnswer() {
+				grpcClient.getAnswerResponse(request, antragraumServiceBlockingStub);
+
+				verify(antragraumServiceBlockingStub).sendRueckfrageAnswer(any(GrpcSendRueckfrageAnswerRequest.class));
+			}
+		}
+
+		@Nested
+		class TestFindRueckfragen {
+			@Mock
+			private GrpcFindRueckfragenRequest request;
+
+			@Test
+			void shouldCallClientFindRueckfragen() {
+				var channelFactory = mock(GrpcChannelFactory.class);
+				var channel = mock(Channel.class);
+				when(channelFactory.createChannel(anyString())).thenReturn(channel);
+				doReturn(channelFactory).when(grpcClient).getChannelFactory(anyString());
+				doReturn(GrpcFindRueckfragenResponse.getDefaultInstance()).when(grpcClient)
+				  .getGrpcFindRueckfragenResponse(any(GrpcFindRueckfragenRequest.class), any());
+
+				grpcClient.getFindRueckfragen(request, CHANNEL_ADDRESS);
+
+				verify(grpcClient).getGrpcFindRueckfragenResponse(any(GrpcFindRueckfragenRequest.class), any());
+			}
+
+			@Test
+			void shouldCallRemoteServiceFindRueckfragen() {
+				grpcClient.getGrpcFindRueckfragenResponse(request, antragraumServiceBlockingStub);
+
+				verify(antragraumServiceBlockingStub).findRueckfragen(any(GrpcFindRueckfragenRequest.class));
+			}
+		}
+	}
+
+	@Nested
+	class TestCheckingCommandStatus {
+		@Mock
+		private CommandServiceGrpc.CommandServiceBlockingStub commandServiceBlockingStub;
+		@Mock
+		private GrpcGetCommandRequest request;
+
+		@Test
+		void shouldCallGetGrpcCommand() {
+			var channelFactory = mock(GrpcChannelFactory.class);
+			var channel = mock(Channel.class);
+			when(channelFactory.createChannel(anyString())).thenReturn(channel);
+			doReturn(channelFactory).when(grpcClient).getChannelFactory(anyString());
+			doReturn(mock(GrpcCommand.class)).when(grpcClient).getGrpcCommand(any(), any(GrpcGetCommandRequest.class));
+
+			grpcClient.getCommand(UUID.randomUUID().toString(), CHANNEL_ADDRESS);
+
+			verify(grpcClient).getGrpcCommand(any(), any(GrpcGetCommandRequest.class));
+		}
+
+		@Test
+		void shouldGetCommand() {
+			grpcClient.getGrpcCommand(commandServiceBlockingStub, request);
+			verify(commandServiceBlockingStub).getCommand(any(GrpcGetCommandRequest.class));
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteServiceITCase.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteServiceITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..390994d87a525a8bfed8c2deec4f9ec186c46f72
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteServiceITCase.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static de.ozgcloud.antragsraum.nachricht.GrpcRueckfragenTestFactory.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import de.ozgcloud.antragsraum.common.SendTimeoutException;
+import lombok.extern.log4j.Log4j2;
+
+@SpringBootTest(properties = {
+  "grpc.server.inProcessName=test2",
+  "grpc.server.port=-1"
+})
+@SpringJUnitConfig(classes = { NachrichtenServiceGrpcTestConfiguration.class })
+@DirtiesContext
+@ExtendWith(MockitoExtension.class)
+@Log4j2
+class NachrichtenRemoteServiceITCase {
+	public static final String ADDRESS = "in-process:test2";
+	public static final String COMMAND_ID = "id";
+	String samlResponse;
+
+	@SpyBean
+	NachrichtenRemoteService nachrichtenRemoteService;
+	@Autowired
+	private GrpcServerInterceptor grpcServerInterceptor;
+
+	@BeforeEach
+	void setupSamlResponse() {
+		try {
+			samlResponse = FileUtils.readFileToString(new File("src/test/resources/testfiles/SamlResponse.xml"), Charset.defaultCharset());
+		} catch (IOException e) {
+			LOG.error("Error reading SAML response data.", e);
+		}
+	}
+
+	@Nested
+	class TestGetNachrichten {
+		@BeforeEach
+		void setup() {
+			initUserPasswordToken();
+		}
+
+		@Test
+		void shouldLoadNachrichten() {
+			var nachricht = nachrichtenRemoteService.findRueckfragen(NachrichtEventTestFactory.POSTFACH_ID, ADDRESS);
+
+			assertThat(nachricht).isNotNull();
+		}
+
+		@Test
+		void shouldHaveRueckfrageId() {
+			var nachrichten = nachrichtenRemoteService.findRueckfragen(NachrichtEventTestFactory.POSTFACH_ID, ADDRESS);
+
+			assertThat(nachrichten.get(0).id()).isEqualTo(RUECKFRAGE_ID);
+		}
+	}
+
+	@Nested
+	class TestSendNachricht {
+		@Test
+		void shouldSendNachricht() {
+			initUserPasswordToken();
+
+			var res = nachrichtenRemoteService.sendAnswer(ReplyNachrichtTestFactory.create(), ADDRESS);
+
+			assertThat(res).isTrue();
+		}
+
+		@Test
+		void shouldCheckCommand() throws Exception {
+			SecurityContextHolder.getContext().setAuthentication(mock(Authentication.class));
+
+			nachrichtenRemoteService.sendAnswer(ReplyNachrichtTestFactory.create(), ADDRESS);
+
+			verify(nachrichtenRemoteService).waitUntilCommandIsFinished(anyString(), anyString());
+			verify(nachrichtenRemoteService).checkCommandIsFinished(anyString(), anyString(), any(Authentication.class));
+		}
+
+		@Test
+		void shouldThrowSendTimeoutException() {
+			initUserPasswordToken();
+
+			assertThatExceptionOfType(SendTimeoutException.class).isThrownBy(() ->
+			  nachrichtenRemoteService.waitUntilCommandIsFinished(ADDRESS, COMMAND_ID)
+			);
+		}
+	}
+
+	private void initUserPasswordToken() {
+		UsernamePasswordAuthenticationToken authentication = mock(UsernamePasswordAuthenticationToken.class);
+		when(authentication.getCredentials()).thenReturn("jwt-token");
+		SecurityContextHolder.getContext().setAuthentication(authentication);
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..05a5e22c4c2a39b2db56d1fdfe6806cdb4e99b8a
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenRemoteServiceTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import de.ozgcloud.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.common.AddressNotFoundException;
+import de.ozgcloud.antragsraum.common.SendTimeoutException;
+import de.ozgcloud.antragsraum.common.TechnicalException;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenRequest;
+import de.ozgcloud.nachrichten.antragraum.GrpcFindRueckfragenResponse;
+import de.ozgcloud.nachrichten.antragraum.GrpcRueckfrageAnswer;
+import de.ozgcloud.nachrichten.antragraum.GrpcSendRueckfrageAnswerRequest;
+import io.grpc.StatusRuntimeException;
+
+@ExtendWith(MockitoExtension.class)
+public class NachrichtenRemoteServiceTest {
+	public static final String ADDRESS = "test";
+	public static final String POSTFACH_ID = UUID.randomUUID().toString();
+	public static final String SAML_TOKEN = "saml-token";
+
+	@Spy
+	@InjectMocks
+	private NachrichtenRemoteService nachrichtenRemoteService;
+
+	@Mock
+	private NachrichtenGrpcClient nachrichtenGrpcClient;
+
+	@Mock
+	private NachrichtMapper nachrichtMapper;
+
+	@Mock
+	private NachrichtProperties nachrichtProperties;
+
+	@Mock
+	private ContextService contextService;
+
+	@Nested
+	class TestNachrichtenAvailable {
+
+		@BeforeEach
+		void init() {
+			when(contextService.getSamlResponse()).thenReturn(SAML_TOKEN);
+			when(nachrichtMapper.fromGrpcRueckfrage(any(), anyString())).thenReturn(NachrichtTestFactory.createNachricht());
+			when(nachrichtenGrpcClient.getFindRueckfragen(any(GrpcFindRueckfragenRequest.class),
+			  anyString())).thenReturn(
+			  GrpcFindRueckfragenResponse.newBuilder().addRueckfrage(GrpcRueckfragenTestFactory.create()).build());
+		}
+
+		@Test
+		void shouldLoadNachrichtById() {
+			var nachricht = nachrichtenRemoteService.findRueckfragen(POSTFACH_ID, ADDRESS);
+
+			assertThat(nachricht).isNotEmpty();
+		}
+
+		@Test
+		void shouldSetSamlToken() {
+			ArgumentCaptor<GrpcFindRueckfragenRequest> requestArgumentCaptor =
+			  ArgumentCaptor.forClass(GrpcFindRueckfragenRequest.class);
+
+			nachrichtenRemoteService.findRueckfragen(POSTFACH_ID, ADDRESS);
+
+			verify(nachrichtenGrpcClient).getFindRueckfragen(requestArgumentCaptor.capture(), anyString());
+			assertThat(requestArgumentCaptor.getValue().getSamlToken()).isNotEmpty();
+		}
+	}
+
+	@Nested
+	class TestNoNachrichtenAvailable {
+
+		@BeforeEach
+		void init() {
+			when(contextService.getSamlResponse()).thenReturn(SAML_TOKEN);
+
+			doThrow(StatusRuntimeException.class).when(nachrichtenGrpcClient)
+			  .getFindRueckfragen(any(GrpcFindRueckfragenRequest.class), anyString());
+		}
+
+		@Test
+		void shouldHandleStatusRuntimeException() {
+			var nachricht = nachrichtenRemoteService.findRueckfragen(POSTFACH_ID, ADDRESS);
+
+			assertThat(nachricht).isEmpty();
+		}
+	}
+
+	@Nested
+	class TestSendAnswer {
+
+		private final ReplyNachricht reply = ReplyNachrichtTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			when(contextService.getSamlResponse()).thenReturn(SAML_TOKEN);
+			when(nachrichtMapper.toGrpcRueckfrageAnswer(any())).thenReturn(GrpcRueckfrageAnswer.newBuilder().build());
+			when(nachrichtenGrpcClient.answerRueckfrage(any(GrpcSendRueckfrageAnswerRequest.class),
+			  anyString())).thenReturn(GrpcRueckfragenTestFactory.createSendRueckfrageAnswerResponse());
+		}
+
+		@Test
+		void shouldSendAnswer() {
+			when(nachrichtenGrpcClient.getCommand(anyString(), anyString())).thenReturn(
+			  GrpcCommandTestFactory.createBuilder().setStatus(GrpcCommandTestFactory.STATUS_FINISHED).build());
+			when(nachrichtProperties.getSendPollIntervalDuration()).thenReturn(Duration.of(1L, ChronoUnit.SECONDS));
+			when(nachrichtProperties.getTimeoutDuration()).thenReturn(Duration.of(2L, ChronoUnit.SECONDS));
+
+			assertThat(nachrichtenRemoteService.sendAnswer(reply, ADDRESS)).isTrue();
+		}
+
+		@Test
+		void shouldSetSamlResponse() {
+			when(nachrichtenGrpcClient.getCommand(anyString(), anyString())).thenReturn(
+			  GrpcCommandTestFactory.createBuilder().setStatus(GrpcCommandTestFactory.STATUS_FINISHED).build());
+			when(nachrichtProperties.getSendPollIntervalDuration()).thenReturn(Duration.of(1L, ChronoUnit.SECONDS));
+			when(nachrichtProperties.getTimeoutDuration()).thenReturn(Duration.of(2L, ChronoUnit.SECONDS));
+
+			ArgumentCaptor<GrpcSendRueckfrageAnswerRequest> requestArgumentCaptor =
+			  ArgumentCaptor.forClass(GrpcSendRueckfrageAnswerRequest.class);
+
+			nachrichtenRemoteService.sendAnswer(reply, ADDRESS);
+
+			verify(nachrichtenGrpcClient).answerRueckfrage(requestArgumentCaptor.capture(), anyString());
+			assertThat(requestArgumentCaptor.getValue().getSamlToken()).isNotEmpty();
+		}
+
+		@Test
+		void shouldThrowTimeoutException() {
+			assertThatExceptionOfType(SendTimeoutException.class).isThrownBy(
+			  () -> nachrichtenRemoteService.sendAnswer(reply, ADDRESS));
+		}
+
+		@Test
+		void shouldThrowTechnicalExceptionBecauseOfInterruptedException() throws Exception {
+			doThrow(InterruptedException.class).when(nachrichtenRemoteService).getSendResult(any());
+
+			assertThatExceptionOfType(TechnicalException.class).isThrownBy(
+			  () -> nachrichtenRemoteService.sendAnswer(reply, ADDRESS));
+		}
+
+		@Test
+		void shouldThrowTechnicalExceptionBecauseOfExecutionException() throws Exception {
+			doThrow(ExecutionException.class).when(nachrichtenRemoteService).getSendResult(any());
+
+			assertThatExceptionOfType(TechnicalException.class).isThrownBy(
+			  () -> nachrichtenRemoteService.sendAnswer(reply, ADDRESS));
+		}
+	}
+
+	@Nested
+	class TestNoTargetAddressAvailable {
+
+		private String address;
+
+		@BeforeEach
+		void init() {
+			when(contextService.getSamlResponse()).thenReturn(SAML_TOKEN);
+			doThrow(AddressNotFoundException.class).when(nachrichtenGrpcClient)
+			  .getFindRueckfragen(any(GrpcFindRueckfragenRequest.class), anyString());
+			address = "static://localhost:9090";
+		}
+
+		@Test
+		void shouldThrowAddressNotFoundException() {
+			assertThatExceptionOfType(AddressNotFoundException.class).isThrownBy(
+			  () -> nachrichtenRemoteService.findRueckfragen(POSTFACH_ID, address)
+			);
+		}
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenServiceGrpcTestConfiguration.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenServiceGrpcTestConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ed65ff486a97335ecc477790ac821b94a6d3963
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenServiceGrpcTestConfiguration.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.mockito.Mockito.*;
+
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.google.common.collect.ImmutableList;
+
+import de.ozgcloud.antragsraum.attachments.FileRemoteService;
+import de.ozgcloud.antragsraum.callcontext.CallContextAttachingInterceptor;
+import de.ozgcloud.antragsraum.callcontext.ContextService;
+import de.ozgcloud.antragsraum.common.AuthenticationHelper;
+import de.ozgcloud.antragsraum.common.ChannelPropertiesFactory;
+import lombok.NonNull;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientHealthAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientMetricAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientSecurityAutoConfiguration;
+import net.devh.boot.grpc.client.autoconfigure.GrpcClientTraceAutoConfiguration;
+import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
+import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
+import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
+import net.devh.boot.grpc.common.autoconfigure.GrpcCommonTraceAutoConfiguration;
+import net.devh.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration;
+import net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration;
+import net.devh.boot.grpc.server.serverfactory.GrpcServerConfigurer;
+
+@Configuration
+@ImportAutoConfiguration({
+  GrpcCommonCodecAutoConfiguration.class,
+  GrpcCommonTraceAutoConfiguration.class,
+
+  GrpcServerAutoConfiguration.class,
+  GrpcServerFactoryAutoConfiguration.class,
+
+  GrpcClientAutoConfiguration.class,
+  GrpcClientHealthAutoConfiguration.class,
+  GrpcClientMetricAutoConfiguration.class,
+  GrpcClientSecurityAutoConfiguration.class,
+  GrpcClientTraceAutoConfiguration.class
+})
+public class NachrichtenServiceGrpcTestConfiguration {
+	@Bean
+	AntragsraumServiceGrpc antragsraumServiceGrpcStub() {
+		return new AntragsraumServiceGrpc();
+	}
+
+	@Bean
+	CommandGrpcService commandGrpcServiceStub() {
+		return new CommandGrpcService();
+	}
+
+	NachrichtProperties nachrichtProperties() {
+		NachrichtProperties nachrichtProperties = new NachrichtProperties();
+		nachrichtProperties.setSendPollInterval("PT-0.5S");
+		nachrichtProperties.setSendTimeout("PT2S");
+		return nachrichtProperties;
+	}
+
+	@Bean
+	NachrichtenRemoteService nachrichtRemoteService() {
+		return new NachrichtenRemoteService(nachrichtenGrpcClient(), nachrichtProperties(), contextService(), nachrichtMapper());
+	}
+
+	NachrichtenGrpcClient nachrichtenGrpcClient() {
+		return new NachrichtenGrpcClient(initGlobalClientInterceptorRegistry(), initChannelsPropertiesFactory());
+	}
+
+	NachrichtMapper nachrichtMapper() {
+		return new NachrichtMapper(mock(FileRemoteService.class));
+	}
+
+	@Bean
+	public ContextService contextService() {
+		return new ContextService(authenticationHelper());
+	}
+
+	@Bean
+	public AuthenticationHelper authenticationHelper() {
+		return new AuthenticationHelper();
+	}
+
+	private GlobalClientInterceptorRegistry initGlobalClientInterceptorRegistry() {
+		GlobalClientInterceptorRegistry globalClientInterceptorRegistry = mock(GlobalClientInterceptorRegistry.class);
+		when(globalClientInterceptorRegistry.getClientInterceptors()).thenReturn(
+		  ImmutableList.of(new CallContextAttachingInterceptor(contextService())));
+
+		return globalClientInterceptorRegistry;
+	}
+
+	@NonNull
+	private static ChannelPropertiesFactory initChannelsPropertiesFactory() {
+		return new ChannelPropertiesFactory(new GrpcChannelsProperties());
+	}
+
+	@Bean
+	public GrpcServerConfigurer keepMetadataInterceptorInterceptor() {
+		return serverBuilder -> serverBuilder.intercept(serverInterceptor());
+	}
+
+	@Bean
+	public GrpcServerInterceptor serverInterceptor() {
+		return new GrpcServerInterceptor();
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e910b0330f75ab6172edd61cae196b75abd40a3
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenServiceTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static de.ozgcloud.antragsraum.nachricht.NachrichtenService.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
+
+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.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import de.ozgcloud.antragsraum.attachments.OzgFile;
+import de.ozgcloud.antragsraum.common.InsufficientTrustLevelException;
+import de.ozgcloud.antragsraum.events.NachrichtEvent;
+import de.ozgcloud.antragsraum.events.NachrichtEventService;
+
+@ExtendWith(MockitoExtension.class)
+class NachrichtenServiceTest {
+	@Spy
+	@InjectMocks
+	private NachrichtenService nachrichtenService;
+	@Mock
+	private NachrichtEventService nachrichtEventService;
+	@Mock
+	private NachrichtenRemoteService nachrichtenRemoteService;
+	@Mock
+	private NachrichtenTrustLevelService nachrichtenTrustLevelService;
+
+	@Nested
+	class TestGetTopicHeadersOfPostfach {
+		@BeforeEach
+		void init() {
+			when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(List.of(NachrichtEventTestFactory.create()));
+			when(nachrichtenRemoteService.findRueckfragen(any(), anyString())).thenReturn(List.of(NachrichtTestFactory.createNachricht()));
+		}
+
+		@Test
+		void shouldReturnTopicHeader() {
+			when(nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(any(NachrichtTrustLevel.class))).thenReturn(false);
+
+			var result = nachrichtenService.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			assertThat(result).hasSize(1);
+		}
+
+		@Test
+		void shouldOnlyAddSelfLink() {
+			var topicHeaderWithSelfLink = NachrichtTestFactory.createTopicHeader();
+			topicHeaderWithSelfLink.add(linkTo(NachrichtenController.class).withSelfRel());
+
+			when(nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(any(NachrichtTrustLevel.class))).thenReturn(false);
+
+			var result = nachrichtenService.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			assertThat(result.get(0)).isEqualTo(topicHeaderWithSelfLink);
+		}
+
+		@Test
+		void shouldAddSelfAndTopicLink() {
+			var fullyLinkedTopicHeader = NachrichtTestFactory.createTopicHeader();
+			fullyLinkedTopicHeader.add(linkTo(NachrichtenController.class).withSelfRel());
+			fullyLinkedTopicHeader.add(linkTo(methodOn(NachrichtenController.class).getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID,
+			  fullyLinkedTopicHeader.getClerkMessage().id())).withRel(TOPIC_LINK_RELATIONSHIP_NAME));
+
+			when(nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(any(NachrichtTrustLevel.class))).thenReturn(true);
+
+			var result = nachrichtenService.getTopicHeadersOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			assertThat(result.get(0)).isEqualTo(fullyLinkedTopicHeader);
+		}
+	}
+
+	@Nested
+	class TestGetTopicOfPostfach {
+		@BeforeEach
+		void init() {
+			final NachrichtEvent event = NachrichtEventTestFactory.create();
+
+			when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(List.of(event));
+
+			when(nachrichtenRemoteService.findRueckfragen(any(), anyString())).thenReturn(List.of(NachrichtTestFactory.createNachrichtBuilder()
+			  .postfachId(event.postfachId())
+			  .vorgangId(NachrichtTestFactory.VORGANG_ID)
+			  .id(NachrichtTestFactory.ID)
+			  .attachments(List.of(OzgFile.builder().id(NachrichtTestFactory.ATTACHMENT_ID_1).build()))
+			  .replyNachrichten(List.of(ReplyNachrichtTestFactory.createBuilder().id(UUID.randomUUID().toString()).build()))
+			  .build())
+			);
+		}
+
+		@Test
+		void shouldThrowInsufficientTrustLevelException() {
+			when(nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(any(NachrichtTrustLevel.class))).thenReturn(false);
+
+			assertThatExceptionOfType(InsufficientTrustLevelException.class).isThrownBy(
+				() -> nachrichtenService.getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID))
+			  .withMessage("Current user does not have the required trust level for the Rueckfrage with id '" + NachrichtTestFactory.ID + "'.");
+		}
+
+		@Test
+		void shouldMapTopic() {
+			when(nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(any(NachrichtTrustLevel.class))).thenReturn(true);
+
+			var result = nachrichtenService.getTopicOfPostfach(NachrichtTestFactory.POSTFACH_ID, NachrichtTestFactory.ID);
+
+			assertThat(result).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestGetRueckfragenOfPostfachWithNachrichtenEventAvailable {
+		final NachrichtEvent event = NachrichtEventTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(List.of(event));
+
+			when(nachrichtenRemoteService.findRueckfragen(any(), anyString())).thenReturn(
+			  List.of(NachrichtTestFactory.createNachrichtBuilder()
+				.postfachId(event.postfachId())
+				.vorgangId(NachrichtTestFactory.VORGANG_ID)
+				.id(NachrichtTestFactory.ID)
+				.attachments(List.of(OzgFile.builder().id(NachrichtTestFactory.ATTACHMENT_ID_1).build()))
+				.replyNachrichten(List.of(ReplyNachrichtTestFactory.createBuilder().id(UUID.randomUUID().toString()).build()))
+				.build())
+			);
+		}
+
+		@Test
+		void shouldCallNachrichtRemoteService() {
+			nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
+
+			verify(nachrichtenRemoteService, atMostOnce()).findRueckfragen(anyString(), anyString());
+		}
+
+		@Test
+		void shouldHaveNachrichten() {
+			var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
+
+			assertThat(nachrichten).hasSize(1);
+		}
+
+		@Test
+		void shouldHaveNachrichtenText() {
+			var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
+
+			assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::message).isNotNull();
+		}
+
+		@Test
+		void shouldHavePostfachId() {
+			var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
+
+			assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::postfachId).hasValue(event.postfachId());
+		}
+
+		@Test
+		void shouldHaveAttachment() {
+			var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
+
+			assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::attachments)
+			  .hasValue(List.of(OzgFile.builder().id(NachrichtTestFactory.ATTACHMENT_ID_1).build()));
+		}
+	}
+
+	@Nested
+	class TestNachrichtWithNullAttachment {
+		final NachrichtEvent event = NachrichtEventTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(
+			  List.of(event)
+			);
+
+			when(nachrichtenRemoteService.findRueckfragen(anyString(), anyString())).thenReturn(
+			  List.of(NachrichtTestFactory.createNachrichtBuilder()
+				.vorgangId(NachrichtTestFactory.VORGANG_ID)
+				.postfachId(event.postfachId())
+				.id(NachrichtTestFactory.ID)
+				.build())
+			);
+		}
+
+		@Test
+		void shouldNotHaveAttachment() {
+			var nachrichten = nachrichtenService.getRueckfragenOfPostfach(event.postfachId());
+
+			assertThat(nachrichten.stream().findFirst()).isPresent().map(Nachricht::attachments).isEmpty();
+		}
+	}
+
+	@Nested
+	class TestGetRueckfragenOfPostfachWithNoNachrichtenEventAvailable {
+		@BeforeEach
+		void init() {
+			when(nachrichtEventService.getNachrichtEventsOfPostfachId(anyString())).thenReturn(List.of());
+		}
+
+		@Test
+		void shouldCallNachrichtEventService() {
+			var nachrichten = nachrichtenService.getRueckfragenOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			assertThat(nachrichten).isEmpty();
+		}
+
+		@Test
+		void shouldNotCallNachrichtRemoteService() {
+			nachrichtenService.getRueckfragenOfPostfach(NachrichtTestFactory.POSTFACH_ID);
+
+			verify(nachrichtenRemoteService, never()).findRueckfragen(anyString(), anyString());
+		}
+	}
+
+	@Nested
+	class TestSendNachricht {
+		ReplyNachricht msg = ReplyNachrichtTestFactory.create();
+
+		@Test
+		void shouldCallRemoteService() {
+			nachrichtenService.sendRueckfrageAnswer(msg);
+
+			verify(nachrichtenRemoteService).sendAnswer(any(ReplyNachricht.class), anyString());
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenTrustLevelServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenTrustLevelServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd35b8ee1278d634700304525c8e719565222ea2
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/NachrichtenTrustLevelServiceTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import de.ozgcloud.antragsraum.security.User;
+import de.ozgcloud.antragsraum.security.UserTrustLevel;
+
+class NachrichtenTrustLevelServiceTest {
+	private NachrichtenTrustLevelService nachrichtenTrustLevelService;
+
+	@Nested
+	class TestNachrichtMatchesUserTrustLevel {
+		@BeforeEach
+		void init() {
+			nachrichtenTrustLevelService = new NachrichtenTrustLevelService();
+		}
+
+		@Test
+		void shouldDenyUsersWithoutTrustLevel() {
+			try (MockedStatic<SecurityContextHolder> securityContextHolder = mockStatic(SecurityContextHolder.class)) {
+				var mockedSecurityContext = getMockedSecurityContext(null);
+				securityContextHolder.when(SecurityContextHolder::getContext).thenReturn(mockedSecurityContext);
+
+				var resultLow = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.LOW);
+				var resultSubstantial = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.SUBSTANTIAL);
+				var resultHigh = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.HIGH);
+
+				assertThat(resultLow).isFalse();
+				assertThat(resultSubstantial).isFalse();
+				assertThat(resultHigh).isFalse();
+			}
+		}
+
+		@Test
+		void shouldAllowNachrichtenWithoutTrustLevel() {
+			try (MockedStatic<SecurityContextHolder> securityContextHolder = mockStatic(SecurityContextHolder.class)) {
+				var mockedSecurityContext = getMockedSecurityContext(UserTrustLevel.STORK_QAA_LEVEL_1);
+				securityContextHolder.when(SecurityContextHolder::getContext).thenReturn(mockedSecurityContext);
+
+				var result = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(null);
+
+				assertThat(result).isTrue();
+			}
+		}
+
+		@Test
+		void shouldAllowLowLevelNachrichten() {
+			try (MockedStatic<SecurityContextHolder> securityContextHolder = mockStatic(SecurityContextHolder.class)) {
+				var mockedSecurityContext = getMockedSecurityContext(UserTrustLevel.STORK_QAA_LEVEL_1);
+				securityContextHolder.when(SecurityContextHolder::getContext).thenReturn(mockedSecurityContext);
+
+				var result = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.LOW);
+
+				assertThat(result).isTrue();
+			}
+		}
+
+		@Test
+		void shouldAllowLevel4Users() {
+			try (MockedStatic<SecurityContextHolder> securityContextHolder = mockStatic(SecurityContextHolder.class)) {
+				var mockedSecurityContext = getMockedSecurityContext(UserTrustLevel.STORK_QAA_LEVEL_4);
+				securityContextHolder.when(SecurityContextHolder::getContext).thenReturn(mockedSecurityContext);
+
+				var resultLow = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.LOW);
+				var resultSubstantial = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.SUBSTANTIAL);
+				var resultHigh = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.HIGH);
+
+				assertThat(resultLow).isTrue();
+				assertThat(resultSubstantial).isTrue();
+				assertThat(resultHigh).isTrue();
+			}
+		}
+
+		@Test
+		void shouldOnlyDenyHighLevelNachrichtenForLevel3Users() {
+			try (MockedStatic<SecurityContextHolder> securityContextHolder = mockStatic(SecurityContextHolder.class)) {
+				var mockedSecurityContext = getMockedSecurityContext(UserTrustLevel.STORK_QAA_LEVEL_3);
+				securityContextHolder.when(SecurityContextHolder::getContext).thenReturn(mockedSecurityContext);
+
+				var resultLow = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.LOW);
+				var resultSubstantial = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.SUBSTANTIAL);
+				var resultHigh = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.HIGH);
+
+				assertThat(resultLow).isTrue();
+				assertThat(resultSubstantial).isTrue();
+				assertThat(resultHigh).isFalse();
+			}
+		}
+
+		@Test
+		void shouldOnlyAllowLowLevelNachrichtenForLevel1Users() {
+			try (MockedStatic<SecurityContextHolder> securityContextHolder = mockStatic(SecurityContextHolder.class)) {
+				var mockedSecurityContext = getMockedSecurityContext(UserTrustLevel.STORK_QAA_LEVEL_1);
+				securityContextHolder.when(SecurityContextHolder::getContext).thenReturn(mockedSecurityContext);
+
+				var resultLow = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.LOW);
+				var resultSubstantial = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.SUBSTANTIAL);
+				var resultHigh = nachrichtenTrustLevelService.nachrichtMatchesUserTrustLevel(NachrichtTrustLevel.HIGH);
+
+				assertThat(resultLow).isTrue();
+				assertThat(resultSubstantial).isFalse();
+				assertThat(resultHigh).isFalse();
+			}
+		}
+
+		private SecurityContext getMockedSecurityContext(UserTrustLevel userTrustLevel) {
+			var authentication = mock(UsernamePasswordAuthenticationToken.class);
+			when(authentication.getPrincipal()).thenReturn(User.builder().trustLevel(userTrustLevel).build());
+			var context = mock(SecurityContext.class);
+			when(context.getAuthentication()).thenReturn(authentication);
+
+			return context;
+		}
+	}
+}
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyNachrichtTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/ReplyNachrichtTestFactory.java
similarity index 50%
rename from ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyNachrichtTestFactory.java
rename to server/src/test/java/de/ozgcloud/antragsraum/nachricht/ReplyNachrichtTestFactory.java
index 2f2368096e1e0751b4cc9d1d4b454df098dce131..1f09e65f6a569516cf3ed13673c2a144aadbafa9 100644
--- a/ozg-antragsraum-server/src/test/java/de/mgm/bup/ozg/antragsraum/nachricht/ReplyNachrichtTestFactory.java
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/ReplyNachrichtTestFactory.java
@@ -1,7 +1,6 @@
 /*
- * 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
+ * Copyright (c) 2023-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 -
@@ -20,25 +19,28 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-
-package de.mgm.bup.ozg.antragsraum.nachricht;
+package de.ozgcloud.antragsraum.nachricht;
 
 import java.util.Date;
 import java.util.UUID;
 
 public class ReplyNachrichtTestFactory {
-    public static final String ID = UUID.randomUUID().toString();
-    public static final long DATE = new Date().getTime();
-    public static final String TEXT = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam";
+	public static final String ID = UUID.randomUUID().toString();
+	public static final String ADDRESS = "static://localhost:9091";
+	public static final long DATE = new Date().getTime();
+	public static final String TEXT = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam";
 
-    public static ReplyNachricht create() {
-        return createBuilder().build();
-    }
+	public static ReplyNachricht create() {
+		return createBuilder().build();
+	}
 
-    public static ReplyNachricht.ReplyNachrichtBuilder createBuilder() {
-        return ReplyNachricht.builder()
-                .id(ID)
-                .date(DATE)
-                .message(TEXT);
-    }
+	public static ReplyNachricht.ReplyNachrichtBuilder createBuilder() {
+		return ReplyNachricht.builder()
+		  .id(ID)
+		  .address(ADDRESS)
+		  .postfachId(NachrichtTestFactory.POSTFACH_ID)
+		  .address(ADDRESS)
+		  .date(DATE)
+		  .message(TEXT);
+	}
 }
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/TopicHeaderMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/TopicHeaderMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf55068eaeada9dd1a53bd23dd76dccc0aff08f4
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/TopicHeaderMapperTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class TopicHeaderMapperTest {
+	@Nested
+	class TestFromNachrichten {
+		private final List<Nachricht> nachrichten = List.of(NachrichtTestFactory.createNachricht(), NachrichtTestFactory.createNachricht());
+
+		@Test
+		void shouldHaveTopicHeaders() {
+			var topicHeaders = TopicHeaderMapper.fromNachrichten(nachrichten);
+
+			assertThat(topicHeaders).hasSize(2);
+		}
+
+		@Test
+		void shouldMapClerkNachrichtHeader() {
+			var topicHeaders = TopicHeaderMapper.fromNachrichten(nachrichten);
+
+			assertThat(topicHeaders.get(0).getClerkMessage()).isNotNull();
+			assertThat(topicHeaders.get(1).getClerkMessage()).isNotNull();
+		}
+
+		@Test
+		void shouldMapVorgangId() {
+			var topicHeaders = TopicHeaderMapper.fromNachrichten(nachrichten);
+
+			assertThat(topicHeaders.get(0).getClerkMessage().id()).isEqualTo(NachrichtTestFactory.ID);
+			assertThat(topicHeaders.get(1).getClerkMessage().id()).isEqualTo(NachrichtTestFactory.ID);
+		}
+
+		@Test
+		void shouldMapVorgangName() {
+			var topicHeaders = TopicHeaderMapper.fromNachrichten(nachrichten);
+
+			assertThat(topicHeaders.get(0).getTopicTitle()).isEqualTo(NachrichtTestFactory.VORGANG_TITLE);
+			assertThat(topicHeaders.get(1).getTopicTitle()).isEqualTo(NachrichtTestFactory.VORGANG_TITLE);
+		}
+
+		@Test
+		void shouldMapTrustLevel() {
+			var topicHeaders = TopicHeaderMapper.fromNachrichten(nachrichten);
+
+			assertThat(topicHeaders.get(0).getTrustLevel()).isEqualTo(NachrichtTrustLevel.LOW);
+			assertThat(topicHeaders.get(1).getTrustLevel()).isEqualTo(NachrichtTrustLevel.LOW);
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/nachricht/TopicMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/TopicMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fb0d24e4e0ffd7865fec6cabdef78f0deeda265
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/nachricht/TopicMapperTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2023-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.antragsraum.nachricht;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class TopicMapperTest {
+	@Nested
+	class TestFromNachricht {
+		private final Nachricht nachricht = NachrichtTestFactory.createNachricht();
+
+		@Test
+		void shouldHaveTopic() {
+			var topic = TopicMapper.fromNachricht(nachricht);
+
+			assertThat(topic).isNotNull();
+		}
+
+		@Test
+		void shouldMapClerkNachricht() {
+			var topic = TopicMapper.fromNachricht(nachricht);
+
+			assertThat(topic.getClerkMessage()).isNotNull();
+		}
+
+		@Test
+		void shouldMapVorgangId() {
+			var topic = TopicMapper.fromNachricht(nachricht);
+
+			assertThat(topic.getClerkMessage().id()).isEqualTo(NachrichtTestFactory.ID);
+		}
+
+		@Test
+		void shouldNotHaveReplyInClerkNachricht() {
+			var topic = TopicMapper.fromNachricht(nachricht);
+
+			assertThat(topic.getClerkMessage().replyNachrichten()).isNull();
+		}
+
+		@Test
+		void shouldMapUserNachrichten() {
+			var topic = TopicMapper.fromNachricht(nachricht);
+
+			assertThat(topic.getUserMessages()).hasSize(1);
+		}
+
+		@Test
+		void shouldMapVorgangName() {
+			var topic = TopicMapper.fromNachricht(nachricht);
+
+			assertThat(topic.getTopicTitle()).isEqualTo(NachrichtTestFactory.VORGANG_TITLE);
+		}
+
+		@Test
+		void shouldMapTrustLevel() {
+			var topic = TopicMapper.fromNachricht(nachricht);
+
+			assertThat(topic.getTrustLevel()).isEqualTo(NachrichtTrustLevel.LOW);
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/AntragsraumLogoutSuccessHandlerTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/AntragsraumLogoutSuccessHandlerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a40f9dc9777133746a647db1d604a2db877fb481
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/AntragsraumLogoutSuccessHandlerTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.antragsraum.security;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+
+import java.io.IOException;
+
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class AntragsraumLogoutSuccessHandlerTest {
+	@Spy
+	@InjectMocks
+	private AntragsraumLogoutSuccessHandler antragsraumLogoutSuccessHandler;
+	@Mock
+	private InMemoryUserDetailService userDetailService;
+
+	@Test
+	void shouldCallLogout() throws ServletException, IOException {
+		Authentication authentication = mock(Authentication.class);
+		when(authentication.getPrincipal()).thenReturn(UserTestFactory.create());
+
+		antragsraumLogoutSuccessHandler.onLogoutSuccess(mock(HttpServletRequest.class), mock(HttpServletResponse.class), authentication);
+
+		verify(userDetailService).logout(any(User.class));
+	}
+
+	@Test
+	void shouldNotCallLogout() throws ServletException, IOException {
+		antragsraumLogoutSuccessHandler.onLogoutSuccess(mock(HttpServletRequest.class), mock(HttpServletResponse.class), mock(Authentication.class));
+
+		verify(userDetailService, never()).logout(any(User.class));
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/AuthenticationControllerTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/AuthenticationControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d2e1fc3172ef83ce0b1f937b45458bf334052b3
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/AuthenticationControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.antragsraum.security;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpStatusCode;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class AuthenticationControllerTest {
+    @Spy
+    @InjectMocks
+    private AuthenticationController authenticationController;
+
+    @Mock
+    private InMemoryUserDetailService inMemoryUserDetailService;
+
+    @Mock
+    private JwtTokenProvider tokenProvider;
+
+    @Nested
+    class TestLogin {
+        private AuthCode code;
+
+        @BeforeEach
+        void setup() {
+            code = new AuthCode(RandomStringUtils.randomAlphanumeric(20));
+        }
+
+        @Test
+        void shouldCreateTokenResponse() {
+            var tokenResponse = authenticationController.login(code);
+
+            assertThat(tokenResponse).isNotNull();
+        }
+
+        @Test
+        void shouldHaveJwtToken() {
+            when(inMemoryUserDetailService.getUserByCode(anyString())).thenReturn(UserTestFactory.create());
+            when(tokenProvider.generate(any(User.class))).thenReturn("test");
+
+            var tokenResponse = authenticationController.login(code);
+
+            assertThat(tokenResponse.getBody()).isNotNull();
+        }
+
+        @Test
+        void shouldHaveStatusOk() {
+            var tokenResponse = authenticationController.login(code);
+
+            assertThat(tokenResponse.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(200));
+        }
+
+        @Test
+        void shouldCallTokenProvider() {
+            when(inMemoryUserDetailService.getUserByCode(anyString())).thenReturn(UserTestFactory.create());
+
+            authenticationController.login(code);
+
+            verify(tokenProvider).generate(any(User.class));
+        }
+
+        @Test
+        void shouldCallUSerService() {
+            authenticationController.login(code);
+
+            verify(inMemoryUserDetailService).getUserByCode(anyString());
+        }
+
+        @Test
+        void shouldThrowSecurityException() {
+            when(tokenProvider.generate(any())).thenThrow(IllegalArgumentException.class);
+
+            assertThatExceptionOfType(SecurityException.class).isThrownBy(() -> authenticationController.login(code));
+        }
+
+    }
+
+    @Nested
+    class TestRefresh {
+        private AuthCode code;
+
+        @BeforeEach
+        void setup() {
+            code = new AuthCode(RandomStringUtils.randomAlphanumeric(20));
+        }
+
+        @Test
+        void shouldCreateToken() {
+            when(inMemoryUserDetailService.getUser(eq(code))).thenReturn(Optional.of(UserTestFactory.create()));
+
+            var token = authenticationController.refresh(code);
+
+            assertThat(token.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(200));
+        }
+
+        @Test
+        void shouldNotCreateToken() {
+            var token = authenticationController.refresh(code);
+
+            assertThat(token.getStatusCode()).isEqualTo(HttpStatusCode.valueOf(403));
+        }
+
+        @Test
+        void shouldCallUserDetailServiceUpdate() {
+            when(inMemoryUserDetailService.getUser(eq(code))).thenReturn(Optional.of(UserTestFactory.create()));
+            
+            authenticationController.refresh(code);
+
+            verify(inMemoryUserDetailService).updateRefreshCodeOf(any(User.class));
+        }
+    }
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/BayernIdSaml2ExtensionTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/BayernIdSaml2ExtensionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..256306bdc586d9d70aa36549ecf1c76e97f18859
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/BayernIdSaml2ExtensionTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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.antragsraum.security;
+
+import static de.ozgcloud.antragsraum.security.BayernIdSaml2Extension.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.xml.namespace.QName;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.opensaml.core.config.InitializationService;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.schema.XSAny;
+import org.opensaml.saml.saml2.core.Extensions;
+
+@ExtendWith(MockitoExtension.class)
+class BayernIdSaml2ExtensionTest {
+	private static final String URN_1_1 = "urn:1.1";
+	private static final String URN_2_2 = "urn.2.2";
+	private static final String ORGANIZATION_DISPLAY_NAME = "TestOrganizationDisplayName";
+
+	@Mock
+	private BayernIdProperties bayernIdProperties;
+	private BayernIdSaml2Extension bayernIdSaml2Extension;
+
+	@Nested
+	class TestCreateMethodElement {
+
+		private XMLObject authMethodObject;
+
+		@BeforeEach
+		void setUp() {
+			bayernIdSaml2Extension = new BayernIdSaml2Extension(bayernIdProperties);
+			authMethodObject = bayernIdSaml2Extension.createMethod("test", "true");
+		}
+
+		@Test
+		void shouldHaveLocalPart() {
+			assertThat(authMethodObject.getElementQName().getLocalPart()).isEqualTo("test");
+		}
+
+		@Test
+		void shouldHaveNamespace() {
+			assertThat(authMethodObject.getElementQName().getNamespaceURI()).isEqualTo(NAMESPACE_URI);
+		}
+
+		@Test
+		void shouldHavePrefix() {
+			assertThat(authMethodObject.getElementQName().getPrefix()).isEqualTo(NAMESPACE_PREFIX);
+		}
+
+		@Test
+		void shouldHaveOneChild() {
+			assertThat(authMethodObject.getOrderedChildren()).hasSize(1);
+		}
+
+		@Test
+		void shouldHaveChildLocalPart() {
+			var element = getChild(authMethodObject);
+			assertThat(element.getElementQName().getLocalPart()).isEqualTo(ENABLED_LOCAL_NAME);
+		}
+
+		@Test
+		void shouldHaveChildPrefix() {
+			var element = getChild(authMethodObject);
+			assertThat(element.getElementQName().getPrefix()).isEqualTo(NAMESPACE_PREFIX);
+		}
+
+		@Test
+		void shouldHaveChildNamespace() {
+			var element = getChild(authMethodObject);
+			assertThat(element.getElementQName().getNamespaceURI()).isEqualTo(NAMESPACE_URI);
+		}
+
+		@Test
+		void shouldHaveValue() {
+			var element = getChild(authMethodObject);
+			assertThat(element.getTextContent()).isEqualTo("true");
+		}
+
+		private XSAny getChild(XMLObject authMethodElement) {
+			return (XSAny) authMethodElement.getOrderedChildren().get(0);
+		}
+	}
+
+	@Nested
+	class TestCreateRequestedAttributes {
+
+		XMLObject requestAttributes;
+
+		@BeforeEach
+		void setUp() throws Exception {
+			InitializationService.initialize();
+			when(bayernIdProperties.getRequestedAttributeUrns()).thenReturn(List.of(URN_1_1, URN_2_2));
+			bayernIdSaml2Extension = new BayernIdSaml2Extension(bayernIdProperties);
+			requestAttributes = bayernIdSaml2Extension.createRequestedAttributes();
+		}
+
+		@Test
+		void shouldHaveLocalPart() {
+			assertThat(requestAttributes.getElementQName().getLocalPart()).isEqualTo(REQUESTED_ATTRIBUTES_LOCAL_NAME);
+		}
+
+		@Test
+		void shouldHaveNamespace() {
+			assertThat(requestAttributes.getElementQName().getNamespaceURI()).isEqualTo(NAMESPACE_URI);
+		}
+
+		@Test
+		void shouldHavePrefix() {
+			assertThat(requestAttributes.getElementQName().getPrefix()).isEqualTo(NAMESPACE_PREFIX);
+		}
+
+		@Test
+		void shouldHaveTwoAttributes() {
+			assertThat(requestAttributes.getOrderedChildren()).hasSize(2);
+		}
+
+		@Test
+		void shouldHaveAttributeUrn1() {
+			assertThat(getAttribute(requestAttributes, URN_1_1)).isPresent();
+		}
+
+		@Test
+		void shouldHaveAttributeUrn2() {
+			assertThat(getAttribute(requestAttributes, URN_2_2)).isPresent();
+		}
+
+		private Optional<XMLObject> getAttribute(XMLObject requestAttributes, String name) {
+			return requestAttributes.getOrderedChildren().stream().filter(child ->
+			  ((XSAny) child).getUnknownAttributes().containsValue(name)
+			).findFirst();
+		}
+	}
+
+	@Nested
+	class TestCreateDisplayInformation {
+
+		XMLObject displayInformation;
+
+		@BeforeEach
+		void setUp() {
+			when(bayernIdProperties.getOrganizationDisplayName()).thenReturn(ORGANIZATION_DISPLAY_NAME);
+			bayernIdSaml2Extension = new BayernIdSaml2Extension(bayernIdProperties);
+			displayInformation = bayernIdSaml2Extension.createDisplayInformation();
+		}
+
+		@Test
+		void shouldHaveLocalPart() {
+			assertThat(displayInformation.getElementQName().getLocalPart()).isEqualTo(DISPLAY_INFORMATION_LOCAL_NAME);
+		}
+
+		@Test
+		void shouldHaveNamespace() {
+			assertThat(displayInformation.getElementQName().getNamespaceURI()).isEqualTo(NAMESPACE_URI);
+		}
+
+		@Test
+		void shouldHavePrefix() {
+			assertThat(displayInformation.getElementQName().getPrefix()).isEqualTo(NAMESPACE_PREFIX);
+		}
+
+		@Test
+		void shouldHaveOneChild() {
+			assertThat(displayInformation.getOrderedChildren()).hasSize(1);
+		}
+
+		@Test
+		void shouldHaveOneGrandChild() {
+			assertThat(displayInformation.getOrderedChildren().get(0).getOrderedChildren()).hasSize(1);
+		}
+
+		@Test
+		void shouldHaveGrandChildLocalPart() {
+			var element = getGrandChild(displayInformation);
+			assertThat(element.getElementQName().getLocalPart()).isEqualTo(ORGANIZATION_DISPLAY_NAME_LOCAL_NAME);
+		}
+
+		@Test
+		void shouldHaveGrandChildPrefix() {
+			var element = getGrandChild(displayInformation);
+			assertThat(element.getElementQName().getPrefix()).isEqualTo(CLASSIC_UI_PREFIX);
+		}
+
+		@Test
+		void shouldHaveGrandChildNamespace() {
+			var element = getGrandChild(displayInformation);
+			assertThat(element.getElementQName().getNamespaceURI()).isEqualTo(CLASSIC_UI_URI);
+		}
+
+		@Test
+		void shouldHaveGrandChildValue() {
+			var element = getGrandChild(displayInformation);
+			assertThat(element.getTextContent()).isEqualTo(ORGANIZATION_DISPLAY_NAME);
+		}
+
+		private XSAny getGrandChild(XMLObject authMethodElement) {
+			return (XSAny) authMethodElement.getOrderedChildren().get(0).getOrderedChildren().get(0);
+		}
+	}
+
+	@Nested
+	class TestCreateExtension {
+
+		Extensions extensions;
+
+		@BeforeEach
+		void setUp() throws Exception {
+			InitializationService.initialize();
+			when(bayernIdProperties.getRequestedAttributeUrns()).thenReturn(List.of(URN_1_1, URN_2_2));
+			when(bayernIdProperties.getAuthnMethods()).thenReturn(Map.of("Username", "true", "SuperSecretThing", "false"));
+			bayernIdSaml2Extension = new BayernIdSaml2Extension(bayernIdProperties);
+			extensions = bayernIdSaml2Extension.createAkdbExtension();
+		}
+
+		@Test
+		void shouldHaveAkdbElement() {
+			var akdbExtension = extensions.getOrderedChildren().get(0);
+
+			assertThat(akdbExtension.getElementQName().getPrefix()).isEqualTo(NAMESPACE_PREFIX);
+			assertThat(akdbExtension.getElementQName().getLocalPart()).isEqualTo(AUTHENTICATION_REQUEST_LOCAL_NAME);
+			assertThat(akdbExtension.getElementQName().getPrefix()).isEqualTo(NAMESPACE_PREFIX);
+			assertThat(akdbExtension.getElementQName().getNamespaceURI()).isEqualTo(NAMESPACE_URI);
+		}
+
+		@Test
+		void shouldHaveAttributeVersion() {
+			XSAny akdbExtension = (XSAny) extensions.getOrderedChildren().get(0);
+
+			assertThat(akdbExtension.getUnknownAttributes()).containsEntry(new QName(VERSION_LOCAL_PART), "2");
+		}
+
+		@Test
+		void shouldHaveAuthMethods() {
+			var authMethods = getChildElement(AUTHN_METHODS_LOCAL_NAME, extensions.getOrderedChildren().get(0));
+
+			assertThat(authMethods).isPresent();
+		}
+
+		@Test
+		void shouldHaveRequestAttributes() {
+			var requestAttributes = getChildElement(REQUESTED_ATTRIBUTES_LOCAL_NAME, extensions.getOrderedChildren().get(0));
+
+			assertThat(requestAttributes).isPresent();
+		}
+
+		@Test
+		void shouldHaveDisplayInformation() {
+			var displayInformation = getChildElement(DISPLAY_INFORMATION_LOCAL_NAME, extensions.getOrderedChildren().get(0));
+
+			assertThat(displayInformation).isPresent();
+		}
+
+		private Optional<XMLObject> getChildElement(String localName, XMLObject extensions) {
+			return extensions.getOrderedChildren().stream().filter(child ->
+			  child.getElementQName().getLocalPart().equals(localName)
+			).findFirst();
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/InMemoryUserDetailServiceTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/InMemoryUserDetailServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c7b9c4e01c945e78df70090b05b5a88fb7c9558
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/InMemoryUserDetailServiceTest.java
@@ -0,0 +1,243 @@
+/*
+ * 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.antragsraum.security;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.testing.FakeTicker;
+import de.ozgcloud.antragsraum.common.NotFoundException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.*;
+
+@ExtendWith(MockitoExtension.class)
+class InMemoryUserDetailServiceTest {
+    private final InMemoryUserDetailService userDetailService = new InMemoryUserDetailService();
+    private final static String TEMP_ID = UUID.randomUUID().toString();
+    private User user;
+
+    @Nested
+    class TestAddingUser {
+        @BeforeEach
+        void setup() {
+            ReflectionTestUtils.setField(userDetailService, "jwtExpirationMinutes", 30L);
+            user = UserTestFactory.create();
+            userDetailService.tmpTokenUserIdCache = CacheBuilder.newBuilder()
+                    .expireAfterAccess(10, TimeUnit.SECONDS)
+                    .build();
+            userDetailService.addUser(TEMP_ID, user);
+        }
+
+        @Test
+        void shouldAddUser() {
+            assertThat(userDetailService.getUser(UserTestFactory.USER_ID)).isNotNull();
+        }
+
+        @Test
+        void shouldSetExpiryTimeDateWhenAddUser() {
+            var date = ZonedDateTime.now(ZoneId.of("UTC")).plusMinutes(30L).toInstant();
+            assertThat(userDetailService.getUser(UserTestFactory.USER_ID).getTokenExpiresAt()).isCloseTo(date, 10);
+        }
+
+        @Test
+        void shouldGetUserByCode() {
+            assertThat(userDetailService.getUserByCode(TEMP_ID)).isNotNull();
+        }
+
+        @Test
+        void shouldThrowNotFoundExceptionWrongTempId() {
+            assertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> userDetailService.getUserByCode("OTHER"));
+        }
+
+        @Test
+        void shouldLoadUser() {
+            userDetailService.addUser(TEMP_ID, user);
+
+            assertThat(userDetailService.loadUserByUsername(UserTestFactory.USER_NAME)).isNotNull();
+        }
+    }
+
+    @Nested
+    class TestSetUser {
+        @BeforeEach
+        void setup() {
+            ReflectionTestUtils.setField(userDetailService, "jwtExpirationMinutes", 30L);
+            user = UserTestFactory.create();
+            userDetailService.tmpTokenUserIdCache = CacheBuilder.newBuilder()
+                    .expireAfterAccess(10, TimeUnit.SECONDS)
+                    .build();
+            userDetailService.setUser(TEMP_ID, user);
+        }
+
+        @Test
+        void shouldSetUser() {
+            assertThat(userDetailService.getUser(UserTestFactory.USER_ID)).isNotNull();
+        }
+
+        @Test
+        void shouldNotSetExpiryTimeDateWhenSetUser() {
+            assertThat(userDetailService.getUser(UserTestFactory.USER_ID).getTokenExpiresAt()).isEqualTo(user.getTokenExpiresAt());
+        }
+    }
+
+    @Nested
+    class TestExpiringCodeCache {
+        FakeTicker ticker = new FakeTicker();
+
+        @BeforeEach
+        void setup() {
+            ReflectionTestUtils.setField(userDetailService, "jwtExpirationMinutes", 30L);
+
+            user = UserTestFactory.create();
+            userDetailService.tmpTokenUserIdCache = CacheBuilder.newBuilder()
+                    .expireAfterAccess(1, TimeUnit.SECONDS)
+                    .ticker(ticker)
+                    .build();
+            userDetailService.addUser(TEMP_ID, user);
+
+            userDetailService.getUserByCode(TEMP_ID);
+            ticker.advance(2, TimeUnit.SECONDS);
+        }
+
+        @Test
+        void shouldThrowExceptionWhenGetUserByCode() {
+            assertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> userDetailService.getUserByCode(TEMP_ID));
+        }
+    }
+
+    @Nested
+    class TestUninitializedCache {
+        @Test
+        void shouldThrowExceptionWhenGetUserByCode() {
+            assertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> userDetailService.getUserByCode(TEMP_ID));
+        }
+    }
+
+    @Nested
+    class TestLoadingSamlToken {
+        @BeforeEach
+        void setup() {
+            user = UserTestFactory.create();
+
+            ReflectionTestUtils.setField(userDetailService, "jwtExpirationMinutes", 30L);
+            userDetailService.addUser(TEMP_ID, user);
+        }
+
+        @Test
+        void shouldReturnToken() {
+            assertThat(userDetailService.getSamlTokenOfUser(user.getId())).isEqualTo(UserTestFactory.SAML_TOKEN);
+        }
+    }
+
+    @Nested
+    class TestHandlingRefreshToken {
+        @BeforeEach
+        void setup() {
+            user = UserTestFactory.create();
+            ReflectionTestUtils.setField(userDetailService, "jwtExpirationMinutes", 30L);
+            userDetailService.addUser(UUID.randomUUID().toString(), user);
+        }
+
+
+        @Test
+        void shouldGetUser() {
+            var updatedUser = userDetailService.getUser(new AuthCode(user.getRefreshCode()));
+
+            assertThat(updatedUser).isPresent();
+        }
+
+        @Test
+        void shouldNotGetUserBecauseUnknownRefreshCode() {
+            var user = userDetailService.getUser(new AuthCode(UUID.randomUUID().toString()));
+
+            assertThat(user).isNotPresent();
+        }
+
+        @Test
+        void shouldNotGetUserBecauseRefreshCodeExpired() {
+            var localUser = UserTestFactory.createBuilder()
+                    .tokenExpiresAt(Date.from(ZonedDateTime.now(ZoneId.of("UTC")).minusMinutes(31L).toInstant()))
+                    .build();
+            userDetailService.setUser(UUID.randomUUID().toString(), localUser);
+
+            var userOptional = userDetailService.getUser(new AuthCode(localUser.getRefreshCode()));
+
+            assertThat(userOptional).isNotPresent();
+        }
+
+        @Test
+        void shouldUpdateRefreshCode() {
+            var user = UserTestFactory.create();
+            userDetailService.updateRefreshCodeOf(user);
+
+            var updatedUser = userDetailService.getUser(user.getId());
+            assertThat(updatedUser.getRefreshCode()).isNotEqualTo(user.getRefreshCode());
+        }
+    }
+
+    @Nested
+    class TestUserMapCleanUp {
+        private User expiredUser;
+        private User validUser;
+
+        @BeforeEach
+        void setup() {
+            ReflectionTestUtils.setField(userDetailService, "jwtExpirationMinutes", 30L);
+            validUser = UserTestFactory.create();
+            userDetailService.setUser(UUID.randomUUID().toString(), validUser);
+            expiredUser = UserTestFactory.createBuilder()
+                    .id(UUID.randomUUID().toString())
+                    .tokenExpiresAt(Date.from(ZonedDateTime.now(ZoneId.of("UTC")).minusMinutes(5L).toInstant())).build();
+            userDetailService.setUser(UUID.randomUUID().toString(), expiredUser);
+        }
+
+        @Test
+        void shouldRemoveExpiredUser() {
+            userDetailService.userCleanUp();
+
+            assertThat(userDetailService.getUser(expiredUser.getId())).isNull();
+        }
+
+        @Test
+        void shouldNotRemoveValidUser() {
+            userDetailService.userCleanUp();
+
+            assertThat(userDetailService.getUser(validUser.getId())).isNotNull();
+        }
+
+        @Test
+        void shouldRemoveOnLogout() {
+            userDetailService.logout(validUser);
+
+            assertThat(userDetailService.getUser(validUser.getId())).isNull();
+        }
+    }
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenFilterTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenFilterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f21b06deca298ab3cb73b86c8a76b6256dbc06c
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenFilterTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.antragsraum.security;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtParser;
+
+@ExtendWith(MockitoExtension.class)
+class JwtTokenFilterTest {
+	@Mock
+	HttpServletRequest request;
+	@Mock
+	HttpServletResponse response;
+	@Mock
+	FilterChain chain;
+	private static final String SECRET = RandomStringUtils.random(256);
+	@Mock
+	private JwtTokenVerifier tokenVerifier;
+	@Mock
+	private InMemoryUserDetailService userDetailService;
+	@Spy
+	@InjectMocks
+	private JwtTokenFilter tokenFilter;
+	private String token;
+
+	@Nested
+	class TestTokenFilterProcessing {
+
+		@BeforeEach
+		void setup() {
+			User user = UserTestFactory.create();
+
+			JwtTokenProvider tokenProvider = new JwtTokenProvider();
+			ReflectionTestUtils.setField(tokenProvider, "jwtSecret", SECRET);
+
+			token = tokenProvider.generate(user);
+		}
+
+		@Test
+		void shouldCallChainDoFilter() throws ServletException, IOException {
+			tokenFilter.doFilterInternal(request, response, chain);
+
+			verify(chain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
+		}
+
+		@Test
+		void shouldThrowException() {
+			when(request.getHeader(anyString())).thenReturn(JwtTokenFilter.TOKEN_PREFIX + " error_token");
+			doThrow(IllegalStateException.class).when(tokenVerifier).validate(anyString());
+
+			assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() ->
+			  tokenFilter.doFilterInternal(request, response, chain)).withMessage("User not found or invalid token");
+		}
+
+		@Test
+		void shouldCallGetJwtFromRequest() throws ServletException, IOException {
+			tokenFilter.doFilterInternal(request, response, chain);
+
+			verify(tokenFilter).getJwtFromRequest(any(HttpServletRequest.class));
+		}
+
+		@Test
+		void shouldCallTokenVerifierValidate() throws ServletException, IOException {
+			when(request.getHeader(JwtTokenFilter.TOKEN_HEADER)).thenReturn(JwtTokenFilter.TOKEN_PREFIX + " " + token);
+			when(tokenFilter.getJwtFromRequest(request)).thenReturn(Optional.of(token));
+
+			tokenFilter.doFilterInternal(request, response, chain);
+
+			verify(tokenVerifier).validate(any());
+		}
+
+		@Test
+		void shouldCallTokenVerifierGetJws() throws ServletException, IOException {
+			when(request.getHeader(JwtTokenFilter.TOKEN_HEADER)).thenReturn(JwtTokenFilter.TOKEN_PREFIX + " " + token);
+			when(tokenFilter.getJwtFromRequest(request)).thenReturn(Optional.of(token));
+			when(tokenVerifier.validate(any())).thenReturn(Optional.of(mock(JwtParser.class)));
+
+			tokenFilter.doFilterInternal(request, response, chain);
+
+			verify(tokenVerifier).getJws(any(), any());
+		}
+
+		@Test
+		void shouldNotCallTokenVerifierGetJws() throws ServletException, IOException {
+			when(request.getHeader(JwtTokenFilter.TOKEN_HEADER)).thenReturn(JwtTokenFilter.TOKEN_PREFIX + " " + token);
+			when(tokenFilter.getJwtFromRequest(request)).thenReturn(Optional.of(token));
+			when(tokenVerifier.validate(any())).thenReturn(Optional.empty());
+
+			tokenFilter.doFilterInternal(request, response, chain);
+
+			verify(tokenVerifier, never()).getJws(any(), any());
+		}
+
+		@Test
+		void shouldCallCreateAuthentication() throws ServletException, IOException {
+			initFilterFlow();
+
+			tokenFilter.doFilterInternal(request, response, chain);
+
+			verify(tokenFilter).createAuthentication(any(HttpServletRequest.class), anyString(), any());
+		}
+
+		@Test
+		void shouldCallSetUserToSecurityContext() throws ServletException, IOException {
+			initFilterFlow();
+
+			tokenFilter.doFilterInternal(request, response, chain);
+
+			verify(tokenFilter).setUserToSecurityContext(any(Authentication.class));
+		}
+
+		private void initFilterFlow() {
+			when(request.getHeader(JwtTokenFilter.TOKEN_HEADER)).thenReturn(JwtTokenFilter.TOKEN_PREFIX + " " + token);
+			when(tokenFilter.getJwtFromRequest(request)).thenReturn(Optional.of(token));
+			when(tokenVerifier.validate(any())).thenReturn(Optional.of(mock(JwtParser.class)));
+			Jws<Claims> jws = mock(Jws.class);
+			Claims claims = mock(Claims.class);
+			when(claims.getSubject()).thenReturn("username");
+			when(jws.getPayload()).thenReturn(claims);
+			when(tokenVerifier.getJws(any(), any())).thenReturn(Optional.of(jws));
+			when(userDetailService.loadUserByUsername(any())).thenReturn(UserTestFactory.create());
+		}
+	}
+
+	@Nested
+	class TestSettingSecurityContext {
+		@BeforeEach
+		void setup() {
+			User user = UserTestFactory.create();
+
+			JwtTokenProvider tokenProvider = new JwtTokenProvider();
+			ReflectionTestUtils.setField(tokenProvider, "jwtSecret", SECRET);
+
+			token = tokenProvider.generate(user);
+
+			when(userDetailService.loadUserByUsername(any())).thenReturn(UserTestFactory.create());
+		}
+
+		@Test
+		void shouldCreateAuthentication() {
+			var auth = tokenFilter.createAuthentication(request, UserTestFactory.USER_NAME, Optional.of(token));
+
+			assertThat(auth).isNotNull();
+		}
+
+		@Test
+		void shouldBeAuthenticated() {
+			var auth = tokenFilter.createAuthentication(request, UserTestFactory.USER_NAME, Optional.of(token));
+
+			assertThat(auth.isAuthenticated()).isTrue();
+		}
+
+		@Test
+		void shouldBeUsernamePasswordAuthenticationToken() {
+			var auth = tokenFilter.createAuthentication(request, UserTestFactory.USER_NAME, Optional.of(token));
+
+			assertThat(auth).isInstanceOf(UsernamePasswordAuthenticationToken.class);
+		}
+
+		@Test
+		void shouldHaveAuthorities() {
+			var auth = tokenFilter.createAuthentication(request, UserTestFactory.USER_NAME, Optional.of(token));
+
+			assertThat(((UsernamePasswordAuthenticationToken) auth).getAuthorities()).isNotNull();
+		}
+
+		@Test
+		void shouldHandleEmptyToken() {
+			var auth = tokenFilter.createAuthentication(request, UserTestFactory.USER_NAME, Optional.empty());
+
+			assertThat(((UsernamePasswordAuthenticationToken) auth).getAuthorities()).isNotNull();
+		}
+	}
+
+	@Nested
+	class TestLoadingTokenFromRequest {
+		@BeforeEach
+		void setup() {
+			User user = UserTestFactory.create();
+
+			JwtTokenProvider tokenProvider = new JwtTokenProvider();
+			ReflectionTestUtils.setField(tokenProvider, "jwtSecret", SECRET);
+
+			token = tokenProvider.generate(user);
+
+			when(request.getHeader(JwtTokenFilter.TOKEN_HEADER)).thenReturn(JwtTokenFilter.TOKEN_PREFIX + token);
+		}
+
+		@Test
+		void shouldGetToken() {
+			var tokenOptional = tokenFilter.getJwtFromRequest(request);
+
+			assertThat(tokenOptional).isPresent();
+		}
+
+		@Test
+		void shouldNotGetToken() {
+			when(request.getHeader(JwtTokenFilter.TOKEN_HEADER)).thenReturn("other " + token);
+
+			var tokenOptional = tokenFilter.getJwtFromRequest(request);
+
+			assertThat(tokenOptional).isNotPresent();
+		}
+
+		@Test
+		void shouldGetTokenValue() {
+			var tokenOptional = tokenFilter.getJwtFromRequest(request);
+
+			assertThat(tokenOptional).isPresent().map(tokenValue -> assertThat(tokenValue).isEqualTo(token));
+		}
+	}
+
+	@Nested
+	class TestNullHandling {
+		@Test
+		void shouldThrowIllegalStateException() {
+			assertThatIllegalArgumentException().isThrownBy(() -> tokenFilter.createAuthentication(null, null, null));
+		}
+
+		@Test
+		void shouldThrowIllegalArgumentException() {
+			assertThatIllegalArgumentException().isThrownBy(() -> tokenFilter.doFilterInternal(null, null, null));
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenProviderTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenProviderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a27a25c9fe947249770ef935d7a59f83e2b6520f
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenProviderTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.antragsraum.security;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Base64;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.assertj.core.data.Percentage;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+@ExtendWith(MockitoExtension.class)
+class JwtTokenProviderTest {
+	private static final String SECRET = RandomStringUtils.random(256);
+
+	JwtTokenProvider provider = new JwtTokenProvider();
+
+	@BeforeEach
+	void setup() {
+		ReflectionTestUtils.setField(provider, "jwtSecret", SECRET);
+	}
+
+	@Nested
+	class TestJwtGeneration {
+		@Test
+		void shouldGenerateToken() {
+			assertThat(provider.generate(UserTestFactory.create())).isNotNull();
+		}
+
+		@Test
+		void shouldExpireIn30Min() {
+			var jwt = provider.generate(UserTestFactory.create());
+			long in30Min = ZonedDateTime.now(ZoneId.of("UTC")).plusMinutes(30L).toInstant().toEpochMilli() / 1000;
+			var expireTimeStamp = getTokenExpireTimeStamp(jwt);
+
+			assertThat(expireTimeStamp).isCloseTo(in30Min, Percentage.withPercentage(5));
+		}
+
+		private Long getTokenExpireTimeStamp(final String jwt) {
+			var decoded = Base64.getDecoder().decode(jwt.split("\\.")[1]);
+			var token = new String(decoded);
+			var expIndex = token.indexOf("\"exp\":");
+			return Long.valueOf(token.substring(expIndex + 6, expIndex + 16));
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenVerifierTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenVerifierTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f8b9e3bff654f9726f4504e3f0e23a3e54a27b1
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/JwtTokenVerifierTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.antragsraum.security;
+
+import static de.ozgcloud.antragsraum.security.JwtTokenProvider.*;
+import static org.assertj.core.api.Assertions.*;
+
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.UUID;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
+
+class JwtTokenVerifierTest {
+	private static final String SECRET = RandomStringUtils.random(256);
+	JwtTokenVerifier verifier = new JwtTokenVerifier();
+
+	@BeforeEach
+	void setup() {
+		ReflectionTestUtils.setField(verifier, "jwtSecret", SECRET);
+	}
+
+	@Nested
+	class TestJwtValidation {
+
+		@Test
+		void shouldValidate() {
+			var token = createToken(SECRET.getBytes(), Date.from(ZonedDateTime.now().plusMinutes(10).toInstant()), UserTestFactory.USER_ID);
+
+			assertThat(verifier.validate(token)).isPresent();
+		}
+
+		@Test
+		void shouldThrowMalformedJwtException() {
+			var token = "sdkjfsdlkfsjkhlf";
+
+			verifier.validate(token).ifPresent(jwtParser ->
+			  assertThatExceptionOfType(MalformedJwtException.class).isThrownBy(() -> jwtParser.parse(token)).isNotNull()
+			);
+		}
+
+		@Test
+		void shouldThrowSignatureException() {
+			var otherSecret = RandomStringUtils.random(256);
+			var token = createToken(otherSecret.getBytes(), Date.from(ZonedDateTime.now().plusMinutes(10).toInstant()), "");
+
+			verifier.validate(token).ifPresent(jwtParser ->
+			  assertThatExceptionOfType(SignatureException.class).isThrownBy(() -> jwtParser.parse(token)).isNotNull()
+			);
+		}
+
+		@Test
+		void shouldThrowExpiredJwtException() {
+			var token = createToken(SECRET.getBytes(), Date.from(ZonedDateTime.now().minusNanos(10).toInstant()), "");
+
+			verifier.validate(token).ifPresent(jwtParser ->
+			  assertThatExceptionOfType(ExpiredJwtException.class).isThrownBy(() -> jwtParser.parse(token)).isNotNull()
+			);
+		}
+
+		@Test
+		void shouldGetClaims() {
+			var token = createToken(SECRET.getBytes(), Date.from(ZonedDateTime.now().plusMinutes(10).toInstant()), UserTestFactory.USER_ID);
+			var parserOptional = verifier.validate(token);
+
+			assertThat(parserOptional).isPresent().map(jwtParser -> assertThatObject(verifier.getJws(jwtParser, token)).isNotNull());
+		}
+
+		private String createToken(byte[] signingKey, Date expiration, String userName) {
+			return Jwts.builder()
+			  .header().add("typ", TOKEN_TYPE)
+			  .and()
+			  .signWith(Keys.hmacShaKeyFor(signingKey), Jwts.SIG.HS512)
+			  .expiration(expiration)
+			  .issuedAt(Date.from(ZonedDateTime.now().toInstant()))
+			  .id(UUID.randomUUID().toString())
+			  .issuer(TOKEN_ISSUER)
+			  .audience().add(TOKEN_AUDIENCE)
+			  .and()
+			  .subject(userName)
+			  .claim("rol", null)
+			  .claim("firstname", UserTestFactory.FIRST_NAME)
+			  .claim("lastname", UserTestFactory.LAST_NAME)
+			  .claim("postkorbhandle", UserTestFactory.POSTKORB_HANDLE)
+			  .claim("trustlevel", UserTestFactory.TRUST_LEVEL.getValue())
+			  .compact();
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/SHA256withRSAAndMGF1SignatureAlgorithmTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/SHA256withRSAAndMGF1SignatureAlgorithmTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8f40ffe89f7d9a50fe2795378dbab1127eb3ec1
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/SHA256withRSAAndMGF1SignatureAlgorithmTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.antragsraum.security;
+
+import static de.ozgcloud.antragsraum.security.SHA256withRSAAndMGF1SignatureAlgorithm.*;
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class SHA256withRSAAndMGF1SignatureAlgorithmTest {
+	@Test
+	void shouldGetKey() {
+		var algorithm = new SHA256withRSAAndMGF1SignatureAlgorithm();
+
+		assertThat(algorithm.getKey()).isEqualTo(RSA_ALGORITHM_ID);
+	}
+
+	@Test
+	void shouldGetURI() {
+		var algorithm = new SHA256withRSAAndMGF1SignatureAlgorithm();
+
+		assertThat(algorithm.getURI()).isEqualTo(RSA_SHA256_MGF1_ALGORITHM_URL);
+	}
+
+	@Test
+	void shouldGetType() {
+		var algorithm = new SHA256withRSAAndMGF1SignatureAlgorithm();
+
+		assertThat(algorithm.getType()).isEqualTo(AlgorithmType.Signature);
+	}
+
+	@Test
+	void shouldGetJCAAlgorithmID() {
+		var algorithm = new SHA256withRSAAndMGF1SignatureAlgorithm();
+
+		assertThat(algorithm.getJCAAlgorithmID()).isEqualTo(RSA_SHA256_MGF1_ALGORITHM_ID);
+	}
+
+	@Test
+	void shouldGetDigest() {
+		var algorithm = new SHA256withRSAAndMGF1SignatureAlgorithm();
+
+		assertThat(algorithm.getDigest()).isEqualTo(SHA256_ALGORITHM_ID);
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/SamlRedirectStrategyTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/SamlRedirectStrategyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..92f1ed4b66f028243221630c326449d6c2c70c2c
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/SamlRedirectStrategyTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.antragsraum.security;
+
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
+import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
+
+@ExtendWith(MockitoExtension.class)
+class SamlRedirectStrategyTest {
+	private static final String URL = "http://test";
+
+	@Mock
+	InMemoryUserDetailService userDetailsService;
+
+	@Mock
+	HttpServletRequest request;
+
+	@Mock
+	HttpServletResponse response;
+
+	@Mock
+	private Saml2Authentication authentication;
+
+	@Mock
+	User user;
+
+	SamlRedirectStrategy samlRedirectStrategy;
+
+	@BeforeEach
+	void setup() {
+		when(request.getContextPath()).thenReturn(URL);
+		when(response.encodeRedirectURL(matches(URL + "\\?code=*"))).thenReturn(URL + "?code=abc");
+		samlRedirectStrategy = new SamlRedirectStrategy(userDetailsService);
+	}
+
+	@Nested
+	class TestRedirect {
+		@Test
+		void shouldAddUserToUserDetailservice() throws IOException {
+			initAuthentication();
+
+			samlRedirectStrategy.sendRedirect(request, response, URL, authentication);
+
+			verify(userDetailsService).addUser(anyString(), any(User.class));
+		}
+
+		private void initAuthentication() {
+			Map<String, List<Object>> attributes = new HashMap<>();
+			attributes.put(UserMapper.NACHNAME_URN, List.of(UserTestFactory.LAST_NAME));
+			attributes.put(UserMapper.VORNAME_URN, List.of(UserTestFactory.FIRST_NAME));
+			attributes.put(UserMapper.POSTKORB_HANDLE_URN, List.of(UserTestFactory.POSTKORB_HANDLE));
+			attributes.put(UserMapper.VERTRAUENSNIVEAU_URN, List.of(UserTestFactory.TRUST_LEVEL.getValue()));
+			attributes.put(UserMapper.BK2_URN, List.of(UserTestFactory.USER_NAME));
+
+			var principal = new DefaultSaml2AuthenticatedPrincipal(UserTestFactory.USER_ID, attributes);
+			when(authentication.getPrincipal()).thenReturn(principal);
+		}
+
+		@Test
+		void shouldCreateRedirectUrl() throws IOException {
+			samlRedirectStrategy.sendRedirect(request, response, URL);
+
+			verify(response).sendRedirect(matches(URL + "\\?code=*"));
+		}
+	}
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/SamlUrlAuthenticationSuccessHandlerTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/SamlUrlAuthenticationSuccessHandlerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a087268e53950e40156d8e396899724bdf2b218d
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/SamlUrlAuthenticationSuccessHandlerTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.antragsraum.security;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+@ExtendWith(MockitoExtension.class)
+class SamlUrlAuthenticationSuccessHandlerTest {
+	private static final String HTTP_TEST = "http://test";
+	private static final String REDIRECT_URL = "http://redirect";
+
+	private SamlUrlAuthenticationSuccessHandler successHandler;
+
+	@Mock
+	private UserDetailsService userService;
+
+	@Nested
+	class TestOnSuccess {
+		@Mock
+		private HttpServletRequest request;
+		@Mock
+		private HttpServletResponse response;
+		@Mock
+		private Authentication authentication;
+
+		@Spy
+		SamlUrlAuthenticationSuccessHandler handler = new SamlUrlAuthenticationSuccessHandler(REDIRECT_URL, userService);
+
+		@BeforeEach
+		void setup() {
+			Collection<? extends GrantedAuthority> grantedAuthorities = List.of(new DefaultRole());
+			doReturn(grantedAuthorities).when(authentication).getAuthorities();
+		}
+
+		@Test
+		void shouldCallHandle() throws IOException {
+			handler.onAuthenticationSuccess(request, response, authentication);
+
+			verify(handler).handle(any(), any(), any());
+		}
+
+		@Test
+		void shouldCallClearAuthenticationAttributes() throws IOException {
+			handler.onAuthenticationSuccess(request, response, authentication);
+
+			verify(handler).clearAuthenticationAttributes(any());
+		}
+	}
+
+	@Nested
+	class TestInit {
+		@Test
+		void shouldGetRedirectUrl() {
+			successHandler = new SamlUrlAuthenticationSuccessHandler(REDIRECT_URL, userService);
+
+			var url = successHandler.getRedirectUrl(REDIRECT_URL);
+
+			assertThat(url).isEqualTo(REDIRECT_URL);
+		}
+
+		@Test
+		void shouldGetDefaultRedirectUrl() {
+			successHandler = new SamlUrlAuthenticationSuccessHandler("", userService);
+
+			var url = successHandler.getRedirectUrl("");
+
+			assertThat(url).isEqualTo("/");
+		}
+	}
+
+	@Nested
+	class TestWithAuthentication {
+		@Mock
+		private Authentication authentication;
+
+		@Test
+		void shouldDetermineTargetUrlForRole() {
+			Collection<? extends GrantedAuthority> grantedAuthorities = List.of(new DefaultRole());
+			doReturn(grantedAuthorities).when(authentication).getAuthorities();
+
+			successHandler = new SamlUrlAuthenticationSuccessHandler("", userService);
+
+			var url = successHandler.determineTargetUrl(authentication);
+
+			assertThat(url).isEqualTo("/");
+		}
+
+		@Test
+		void shouldDetermineTargetUrlMissingRole() {
+			Collection<? extends GrantedAuthority> grantedAuthorities = List.of();
+			doReturn(grantedAuthorities).when(authentication).getAuthorities();
+
+			successHandler = new SamlUrlAuthenticationSuccessHandler(REDIRECT_URL, userService);
+
+			assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> successHandler.determineTargetUrl(authentication))
+			  .withMessage("Invalid role! User is missing role ROLE_USER");
+		}
+	}
+
+	@Nested
+	class TestClearLoginSession {
+		@Mock
+		private HttpServletRequest request;
+
+		@Mock
+		private HttpSession session;
+
+		@Test
+		void shouldClearSession() {
+			when(request.getSession(anyBoolean())).thenReturn(session);
+
+			successHandler = new SamlUrlAuthenticationSuccessHandler(REDIRECT_URL, userService);
+
+			successHandler.clearAuthenticationAttributes(request);
+
+			verify(session).removeAttribute(any());
+		}
+
+		@Test
+		void shouldHandleNullSession() {
+			successHandler = new SamlUrlAuthenticationSuccessHandler(REDIRECT_URL, userService);
+
+			successHandler.clearAuthenticationAttributes(request);
+		}
+	}
+
+	@Nested
+	class TestHandle {
+		@Mock
+		private HttpServletRequest request;
+		@Mock
+		private HttpServletResponse response;
+		@Mock
+		private Authentication authentication;
+
+		@BeforeEach
+		void setup() {
+			Collection<? extends GrantedAuthority> grantedAuthorities = List.of(new DefaultRole());
+			doReturn(grantedAuthorities).when(authentication).getAuthorities();
+		}
+
+		@Test
+		void shouldHandle() throws IOException {
+			when(request.getContextPath()).thenReturn(HTTP_TEST);
+			when(response.isCommitted()).thenReturn(Boolean.FALSE);
+			when(response.encodeRedirectURL(anyString())).thenReturn(HTTP_TEST + "/?code=");
+
+			successHandler = new SamlUrlAuthenticationSuccessHandler("", userService);
+			successHandler.handle(request, response, authentication);
+
+			verify(response).encodeRedirectURL(startsWith(HTTP_TEST + "/?code="));
+			verify(response).sendRedirect(startsWith(HTTP_TEST + "/?code="));
+		}
+
+		@Test
+		void shouldHandleCommitted() throws IOException {
+			when(response.isCommitted()).thenReturn(Boolean.TRUE);
+			successHandler = new SamlUrlAuthenticationSuccessHandler(REDIRECT_URL, userService);
+			successHandler.handle(request, response, authentication);
+
+			verify(response, never()).sendRedirect(anyString());
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/SecurityProviderTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/SecurityProviderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb53cdcea859cb548193f8e954eb05fc67d77108
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/SecurityProviderTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.antragsraum.security;
+
+import static de.ozgcloud.antragsraum.security.SHA256withRSAAndMGF1SignatureAlgorithm.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.security.Security;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.opensaml.core.config.ConfigurationService;
+import org.opensaml.xmlsec.algorithm.AlgorithmRegistry;
+
+public class SecurityProviderTest {
+	private static final String BOUNCY_CASTLE_PROVIDER_ID = "BC";
+
+	private final SecurityProvider securityProvider = new SecurityProvider();
+
+	@Nested
+	class TestAfterPropertiesSet {
+		@BeforeEach
+		void init() {
+			Security.removeProvider(BOUNCY_CASTLE_PROVIDER_ID);
+		}
+
+		@Test
+		void shouldNotAddBouncyCastleProvider() {
+			assertThat(Security.getProvider(BOUNCY_CASTLE_PROVIDER_ID)).isNull();
+		}
+
+		@Test
+		void shouldAddBouncyCastleProvider() {
+			securityProvider.afterPropertiesSet();
+
+			assertThat(Security.getProvider(BOUNCY_CASTLE_PROVIDER_ID)).isInstanceOf(BouncyCastleProvider.class);
+		}
+
+		@Test
+		void shouldHandleAlgorithmRegistryIsNull() {
+			securityProvider.afterPropertiesSet();
+
+			assertThat(ConfigurationService.get(AlgorithmRegistry.class)).isNull();
+		}
+
+		@Test
+		void shouldNotRegisterMukSignatureAlgorithm() {
+			try (var configService = mockStatic(ConfigurationService.class)) {
+				configService.when(() -> ConfigurationService.get(AlgorithmRegistry.class)).thenReturn(new AlgorithmRegistry());
+
+				assertThat(ConfigurationService.get(AlgorithmRegistry.class).get(RSA_SHA256_MGF1_ALGORITHM_URL)).isNull();
+			}
+		}
+
+		@Test
+		void shouldRegisterMukSignatureAlgorithm() {
+			try (var configService = mockStatic(ConfigurationService.class)) {
+				configService.when(() -> ConfigurationService.get(AlgorithmRegistry.class)).thenReturn(new AlgorithmRegistry());
+
+				securityProvider.afterPropertiesSet();
+
+				assertThat(ConfigurationService.get(AlgorithmRegistry.class).get(RSA_SHA256_MGF1_ALGORITHM_URL)).isInstanceOf(
+				  SHA256withRSAAndMGF1SignatureAlgorithm.class);
+			}
+		}
+	}
+}
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/UserMapperTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/UserMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1678e1c25f64b78a91e80b05a7aff3f3dc509a7f
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/UserMapperTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.antragsraum.security;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
+import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class UserMapperTest {
+    static final String SAML_TOKEN = "token";
+    @Mock
+    private Saml2Authentication auth;
+
+    Map<String, List<Object>> attributes;
+
+    @BeforeEach
+    void setup() {
+        when(auth.getSaml2Response()).thenReturn(SAML_TOKEN);
+
+        attributes = new HashMap<>();
+        attributes.put(UserMapper.NACHNAME_URN, List.of(UserTestFactory.LAST_NAME));
+        attributes.put(UserMapper.VORNAME_URN, List.of(UserTestFactory.FIRST_NAME));
+        attributes.put(UserMapper.POSTKORB_HANDLE_URN, List.of(UserTestFactory.POSTKORB_HANDLE));
+        attributes.put(UserMapper.VERTRAUENSNIVEAU_URN, List.of(UserTestFactory.TRUST_LEVEL.getValue()));
+        attributes.put(UserMapper.BK2_URN, List.of(UserTestFactory.USER_NAME));
+
+        var principal = new DefaultSaml2AuthenticatedPrincipal(UserTestFactory.USER_ID, attributes);
+        when(auth.getPrincipal()).thenReturn(principal);
+    }
+
+    @Test
+    void shouldGetUsername() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getUsername()).isEqualTo(UserTestFactory.USER_ID);
+    }
+
+    @Test
+    void shouldGetFirstName() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getFirstName()).isEqualTo(UserTestFactory.FIRST_NAME);
+    }
+
+    @Test
+    void shouldGetLastName() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getLastName()).isEqualTo(UserTestFactory.LAST_NAME);
+    }
+
+    @Test
+    void shouldGetPostkornHandle() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getPostkorbHandle()).isEqualTo(UserTestFactory.POSTKORB_HANDLE);
+    }
+
+    @Test
+    void shouldGetTrustLevel() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getTrustLevel()).isEqualTo(UserTestFactory.TRUST_LEVEL);
+    }
+
+    @Test
+    void shouldGetSaml2Token() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getSamlToken()).isEqualTo(SAML_TOKEN);
+    }
+
+    @Test
+    void shouldGetUnknownAttributesEmpty() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getUnknownAttributes()).isEmpty();
+    }
+
+    @Test
+    void shouldGetUnknownAttributes() {
+        attributes.put("other", List.of("value"));
+        var principal = new DefaultSaml2AuthenticatedPrincipal(UserTestFactory.USER_ID, attributes);
+        when(auth.getPrincipal()).thenReturn(principal);
+
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getUnknownAttributes()).isNotEmpty().hasSize(1);
+    }
+
+    @Test
+    void shouldSetRefreshCode() {
+        var user = UserMapper.map(auth);
+
+        assertThat(user.getRefreshCode()).isNotEmpty();
+    }
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/UserTest.java b/server/src/test/java/de/ozgcloud/antragsraum/security/UserTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..715c7b9af3cf2868b15ca14420aa5140194beef7
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/UserTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.antragsraum.security;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.*;
+
+class UserTest {
+
+    @Test
+    void getAuthorities() {
+        var user = UserTestFactory.create();
+
+        assertThat(user.getAuthorities()).isNotNull();
+        assertThat(user.getAuthorities().iterator().next().getAuthority()).isEqualTo(DefaultRole.ROLE);
+    }
+
+    @Test
+    void credentialsShouldBeExpired() {
+        var expiredUser = UserTestFactory.createBuilder().tokenExpiresAt(Date.from(LocalDateTime.now().minusDays(1).toInstant(ZoneOffset.UTC))).build();
+
+        assertThat(expiredUser.isCredentialsNonExpired()).isFalse();
+    }
+
+    @Test
+    void credentialsShouldNotBeExpired() {
+        var user = UserTestFactory.createBuilder().tokenExpiresAt(Date.from(LocalDateTime.now().plusHours(1).toInstant(ZoneOffset.UTC))).build();
+
+        assertThat(user.isCredentialsNonExpired()).isTrue();
+    }
+}
\ No newline at end of file
diff --git a/server/src/test/java/de/ozgcloud/antragsraum/security/UserTestFactory.java b/server/src/test/java/de/ozgcloud/antragsraum/security/UserTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..2507fae953178f44bfee548e701080ba0c0723ac
--- /dev/null
+++ b/server/src/test/java/de/ozgcloud/antragsraum/security/UserTestFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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.antragsraum.security;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.UUID;
+
+public class UserTestFactory {
+	static final String USER_ID = UUID.randomUUID().toString();
+	static final String REFRESH_TOKEN = UUID.randomUUID().toString();
+	static final String FIRST_NAME = "Paul";
+	static final String LAST_NAME = "Panter";
+	static final String USER_NAME = FIRST_NAME + "_" + LAST_NAME;
+	static final String POSTKORB_HANDLE = UUID.randomUUID().toString();
+	static final UserTrustLevel TRUST_LEVEL = UserTrustLevel.STORK_QAA_LEVEL_3;
+	static final String SAML_TOKEN = "saml";
+	static final Date TOKEN_EXPIRATION = Date.from(ZonedDateTime.now(ZoneId.of("UTC")).plusMinutes(30L).toInstant());
+
+	public static User create() {
+		return createBuilder().build();
+	}
+
+	static User.UserBuilder createBuilder() {
+		return new User.UserBuilder()
+		  .id(USER_ID)
+		  .username(USER_NAME)
+		  .firstName(FIRST_NAME)
+		  .lastName(LAST_NAME)
+		  .postkorbHandle(POSTKORB_HANDLE)
+		  .refreshCode(REFRESH_TOKEN)
+		  .samlToken(SAML_TOKEN)
+		  .tokenExpiresAt(TOKEN_EXPIRATION)
+		  .trustLevel(TRUST_LEVEL);
+	}
+}
diff --git a/server/src/test/resources/application.yml b/server/src/test/resources/application.yml
new file mode 100644
index 0000000000000000000000000000000000000000..20fada7906e5fd46569616d28d0bbb9a65f7b7d8
--- /dev/null
+++ b/server/src/test/resources/application.yml
@@ -0,0 +1,26 @@
+spring:
+  jackson:
+    deserialization:
+      adjust-dates-to-context-time-zone: false
+  servlet:
+    multipart:
+      max-file-size: 150MB
+      max-request-size: 2GB
+  data:
+    mongodb:
+      database: InformationManager
+      port: ${mongodb.container.port}
+      host: localhost
+      auto-index-creation: true
+management:
+  server:
+    port: 8081
+grpc:
+  client:
+    info-manager:
+      address: in-process:test
+      negotiation-type: PLAINTEXT
+logging:
+  level:
+    org.springframework.security: DEBUG
+    springframework.web.client.RestTemplate: DEBUG
diff --git a/server/src/test/resources/testcontainers.properties b/server/src/test/resources/testcontainers.properties
new file mode 100755
index 0000000000000000000000000000000000000000..55860d0df37b9a7f5ef3f095372d4ae5b1806e09
--- /dev/null
+++ b/server/src/test/resources/testcontainers.properties
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 2023-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.
+#
+hub.image.name.prefix=docker-repos.dockerregistry.mgm-tp.com/
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/resources/testfiles/OzgFile.json b/server/src/test/resources/testfiles/OzgFile.json
similarity index 100%
rename from ozg-antragsraum-server/src/test/resources/testfiles/OzgFile.json
rename to server/src/test/resources/testfiles/OzgFile.json
diff --git a/ozg-antragsraum-server/src/test/resources/testfiles/OzgNachricht.json b/server/src/test/resources/testfiles/OzgNachricht.json
similarity index 100%
rename from ozg-antragsraum-server/src/test/resources/testfiles/OzgNachricht.json
rename to server/src/test/resources/testfiles/OzgNachricht.json
diff --git a/server/src/test/resources/testfiles/SamlResponse.xml b/server/src/test/resources/testfiles/SamlResponse.xml
new file mode 100644
index 0000000000000000000000000000000000000000..934a217d796195f08dfa31b0d87a6b8459d32f2c
--- /dev/null
+++ b/server/src/test/resources/testfiles/SamlResponse.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?><saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://deep-touching-condor.ngrok-free.app/login/saml2/sso/bayernid" ID="_d75103771f4e3869ca4bf743efb51320" InResponseTo="ARQf371368-b6eb-4708-b90d-e8a9c5fc0ffd" IssueInstant="2024-02-07T10:27:18.456Z" Version="2.0"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://infra-pre-id.bayernportal.de/idp</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#_d75103771f4e3869ca4bf743efb51320"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>y8O2/uKwgap3hb7Ym/sn+v0e3l+w0Z+wIFe11xXkSHU=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>G4fZCUS7Z+7PwDF7J+ZwXssM+iHBgxt34Uf4U3PWrbaYROrCZFD3hlVwCj35Z7RlQkDBi4q1m9RW6XGGVx2vpUDjT9dTkfbF7tB9PXa6l4mq3RyxuRELMwRpcbnamfe02qwtp0N7n9+gdjTVPb2xTMhp7FVG3OZ46OKwwJIm6jNLE+zVbKkNmxnv8XqGK+FgDS82CCG6Zi8nIZZkR80vHuRnSwrpStiInWSURoIYvG8nQfJ6u6IxbtMkDPtLrQHP6th9NMEyODe4RrjNwH8ERkbBl+rvtz406y3hngOW4uxNSTdQGOWj68t7LSn78S+Zc+5g/8up8gRIY6FWB5QxTl+GINIskcoWEfpyQcY932Jh9jGKFRBj2bcP0xALOeP+LTAz1O3hY0EZD0HpjILNhjp4/4Ki6SSeoVrp4UdEZGPpfFAMXdA9unjQGf5DqT3los5mH+KgkpAQoIU0725tIJuGojigXDIKgbNftB1oXjepcqcWvdnbRZlE9Kk4iU2YcVKGxHtEGi03+Qr2M37SqnooXw94Q0LxOQHU0jaOuw+nA8JbcvbpmHVbh7Qyg6OfrI/g+1pwhaQWrL6zEDDlgF3Fj6QxZGhMviCf43WJd8nPPwLIp0dFxXmbX5yBnpAPC4txJkf4idH8gze054O0Zf9G35vFH8oxELrA+d3qbPY=</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIFbzCCA1egAwIBAgIJAPdFXXarkBN2MA0GCSqGSIb3DQEBCwUAME4xCzAJBgNVBAYTAkRFMQ8w
+DQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMQ0wCwYDVQQKDARBS0RCMQwwCgYDVQQL
+DANJRE0wHhcNMjAxMDI3MTMxODQxWhcNMjUxMDI2MTMxODQxWjBOMQswCQYDVQQGEwJERTEPMA0G
+A1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjENMAsGA1UECgwEQUtEQjEMMAoGA1UECwwD
+SURNMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzDtWAEdC3J9FD+ti1exRhN1lzNgK
+WqO2gQNdJvlt7KGHA2VGGO7tqRogTuoqi/ydtiHJ8+lhp4kcWqyfv7i9HXOncvcsRRmRdZjUY2Iu
+i6ozJqD5LVm/vP5YfdP7vQPdbqyyfpoJhf3mbMEtdNDdGRnGIPUfDn+CFbo37f9tPwMgf3jgh4gx
+aujtLIhhr9gevVTEeZAFu9EvzLNd3kEtRb7MuXqIOdu1rW8HlGYFwwVLqEyBn8XG0QAIfhMmGjFM
+G7z+Kco2quwOmmZVzWQfeH/3AlN2KbcPt7j+pl+6Bew2AAivP7O+95YKORqQjTu3rPWMF4txPId3
+7MSjoytwBRyd5EACTvhQBOGrDFKQUOx6fTtRc8+7XGVz8MdQaZQWQXXh1ByU783twNdnRSrSVIyL
+djiy1uCbjvsSAtbzGBygPIvDo3skCNLNFXsChtHIfFFDK20KPGb0ghEDf2q3hDbFG3ZDGGynZmJc
+ZKuZhJqodJ/++sAXADyTJNAPVYDjKCF4ypELp2Eu/p1gaQPJEb74L/ZFZVOEJFyXIiaqB9J+fcn/
+biqHHOmcCi8n9aIiNt1fatr1Z4lQRWoGtKaGU0+bzUSH4Bgs2EG4u1CI2MKDWqK2aEsHrtu8tbS9
+LrUmDVKtaEUOeul8xWVa036vp/YUIdiJNZSxZG4iTmSOATECAwEAAaNQME4wHQYDVR0OBBYEFFYe
+ltslkaolOmcINXQeSe7nURwpMB8GA1UdIwQYMBaAFFYeltslkaolOmcINXQeSe7nURwpMAwGA1Ud
+EwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAKqAlXoO41SAiycYUOrR90pfwTCysmbtHF5RWSCM
+jF2aCG8URJ7bXwC0lBH8E5zCetFZwdqZziQtxzRkIOfhS5uWbH0RDhwuxZG+5RTPyaHPAZI6e5xH
+Du8vHl/VbC3lnL/6K8l+Purr/yo8qkJqrPgThZRL9jBQyYRhDSsJUyIw5zcKKUQC/JWtMQAQcopb
+jekCs6xDT1HqIN90Sc/gOfYjNo0dGMNmro9mxcw82Iow18KNVdtEexfD+/6x4NPD61pzuQEe09TR
++Cv3XyzBoGQ/2arijcPnGvth79ffVFtRSf3fSs7wEKV9g3mEWXFDtPBhDj6K0kKU/kJfEZixkXl9
+2MY+bmugrtTIrazjtfrgMglIAHu9XCYWd/gef0J+PNfHsxgbTEr3XSC+5/xoFKPQSw3PgV8lkUDq
+4mJUKy/q4YmA37XQxourFR5pWvF03YACdtq6zPjtVeI7Cvkte6k0YW5S3cx9RmPv6YZhlaZ5ERpW
+Niv6IjokLsvNeemf2PApjO7Q2EDBIoHBYH31wwJSsyRDrSVmbaqLFI15fLXeh2A4YbaBDZdGvDiL
+OAk+dG1wdZ2aGw/uNBzMtc8VeKqI1HPcqIluBA3uUPpyLLA+9hDPf6Pp4j0gkXxBikz+/h22bFxE
+1HmDiOSkEn+2NmOHuEFeA+D8jsCAL5VJ3emK</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status><saml2:EncryptedAssertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="_baed1174200b81b1bff3856cb4e6365c" Type="http://www.w3.org/2001/04/xmlenc#Element"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><xenc:EncryptedKey Id="_5a164760d15a61d269e1f7fdd9872a10" Recipient="https://antragsraum.ozgcloud.de/" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/></xenc:EncryptionMethod><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDsTCCApmgAwIBAgIUdw/27be5+2vj+MhGtoJjDsMsdDEwDQYJKoZIhvcNAQELBQAwaDELMAkG
+A1UEBhMCREUxDzANBgNVBAgMBkJheWVybjERMA8GA1UEBwwITXVlbmNoZW4xDzANBgNVBAoMBm1n
+bSB0cDEkMCIGCSqGSIb3DQEJARYVamVucy5yZWVzZUBtZ20tdHAuY29tMB4XDTI0MDExNjEyMjI0
+OVoXDTI1MDExNTEyMjI0OVowaDELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJheWVybjERMA8GA1UE
+BwwITXVlbmNoZW4xDzANBgNVBAoMBm1nbSB0cDEkMCIGCSqGSIb3DQEJARYVamVucy5yZWVzZUBt
+Z20tdHAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/HBBWBDSrEgdwXkSy15V
+00EaVTyLgc4vh/JcDiGIYZSqmcMwBd+B1u36xbdBf/duEtCUymMNP48OMjgFZtR6xn0meuR4NR6Y
+kn9mYGdU/GhldGuGv9XLAEAkVuTlo0H1QYyBS/6JwKQoSsHDkJ3YwDwKcyOt7QtpSadRZjQEN3gD
+vWoRYjgXTxj2I1ovllmi0zOHsFi5PBIuiPWUdJvBrHxpD/XVS9R/qzJpHPu3bjQ6UVRmhiZCUF7H
+5F/PQNwk+qXvjV0ooBeSWWO5hywhk4OP4QEgbYMOSo20YukYX8TJEsum1pwIcQrw7kW4GyKaAycy
+Rsa1fbM3tEkj+TiBKwIDAQABo1MwUTAdBgNVHQ4EFgQUfDL/6R33SJodsONCvxKy96AtU18wHwYD
+VR0jBBgwFoAUfDL/6R33SJodsONCvxKy96AtU18wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQsFAAOCAQEA+PCnvSwKU+bArTCIg5lfrwONbzKkjvPUymDNYX3oj1wVEN75hNf0RD7Rr0//ZYT3
+Rt0G193gjDcH1gbGIYhMLeGGkxEous2l3O+pRIQRR+hprjr6HzF8IphaJy1RbDwyGsXyLcyOylPL
+4cX9IjUdhklHiLZusBq95LSyw7hsCOAL2+vn816O7yv+28EWXXbnP2XEUjW36nxcZvR6oTJUplXy
+HRuuJJTsOxGRNuXA3UVgNbkdm1HnoSGpnsGdUKsUFoEmEJkcSdQRwxeH21WzYGOZmKMcvx2gObaS
+P8tafWh5z4Jx+Z7z5WP72Jt44/lnVjaV8aGo0KHXwgqQOtYftQ==</ds:X509Certificate></ds:X509Data></ds:KeyInfo><xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:CipherValue>ffr9pG/yL4QGQ4o1z/t6HH5XRG8pMHHjzlVTq6uC4eRpVvaNMz8XpUXqNAFGiB0Xbpkm++qOhGsOuz5Wffq5Qo78fMBfU95L1Lk9cVH1pUFfYyz5GV1LqlhStAZrCGHUdv5d0O7JLKgbi45JxxTc7ErAwPlOMqKLs95ZJuhl8Fp9XcYrdzW9IjuwmkB/HyPyjBWV066gaCMLImeBdCzBZc0pxuvH9jq8eX7h1B1eCd5F1LIoj35YDeU3PA/P/E6tLBxdGLFws+nYqNU3B5R2FPPoW+LP9zM7Q+SR20ti1Uh6TEMha05sJjWXFJU78PpJAtEl978ifqqO/23lYXYCrA==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedKey></ds:KeyInfo><xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:CipherValue>YvHcO5br8vWMo/vreVcU8MQylgUmU6vxmvEoUJ07+8kSV0prHD46TtuXHXsLnsYKQMs+SM6bnTWMooNxIy8b0/BvIG4lg/l3m+SwoP56+lOMx26mTEwz9dJT+4BTFH25i3GbWQd86AbD4wT2X9juExE1t6uq1bhACqgeA1erhy/fjTS+1fKKbOTdC0DF35idejruc/YFyV/BNRMHncfj5BnFQFUmXT0b2Fg+WyUAIzflqwXmUhdzmQNl2uqZDL2Vaw4vQp1a9S0ePD3t+QCSGtCYcmqvg+FhooS1KfhqITNinzi07KaQNW6sY0uXpnq3/BGWxXrU58rmU24AQ5ozAgwPfpmOnZhyUURSikvKmvcpD8DPy8jMOxi+rd/tYlxokjPjFSV8ixh0fGZl+7mDAt/YvxBOSSln3nUEMVoKMNgUT3ax6cyUTBXOxt1EMLYuAcHRIBpF2R9MYasmaaR1RntlkKPiomaPdGqIgh1HtkmuwkS6npAH68d0ObD/SpRqu+vPuu++RxHPlbIjShbJWYUJIqPJlhtG7/p9VkC3514t/H1cgxALKstC95OXNH8S6b4m+atl1Wf4PELfHD7lT/QgYHwZk2/xhoWduKc18nauB4kc3v3FZ8nIO3iCgBkXT8qq+/1wJgOnVqAEFWs8w8AC99gHoTgm04q+JUw9XramIXEOdJ10/2Y8HW97XCt53KG+kbaJZe7DISx+2eh79ed3BxEzwgqcXlsrIVcezXWJAl2myFxUggn3c/N1ckgOWFwENOSDFXS6WAtSo/8dlXYvgZTYyKVvVO7l5oq5al2ESrFkHqB3WKu0q9LaWEKw7jMy6FnOawSOjJ2t5o43hfqs9+Wg3J7bgM8YjDuuNZIJ55SO7NIdeeFOkkgGMgcmF0svsxGKbHhdkvqa0t0BLtase7dNIgfWY5laXU7xBn+HEhrz9ZR1xnk8gK28KZwR1wO72rpcV9igsbek5cxEJ/hAk9NSHw7VXAgXZJ6VKZapJfoOqanE50FfK8z0TaoKBUbc1kfOAFnB0n9nUKpUXyyqfiu12w4n3lAs3tBB2S4w3UW5Mef1ZGdF8dqs/T0GUs819vrwYENVUM8u1usP44MnMdTw2i3eo6/VYrCOo1tsa31t46BbLEnercEdGDgpOOBjuda+C3G4NtlDEFzM8rN2FspLS5wphCvKiTB867Q9nrsXOQdDTiwXs2wOpVq1utASdV82Yam1ucVVfriDWlLlhFsFpZnUmwG4zwCPqah2t8inSwx9jramTbJnXpihRw75mFGFC5QAalnV/EAtZ58ecIz4PVTRq4zP7OKtwnN+qIOMm//8AOl6e5H/At7qWB5/dVjw8BUdIxaUF+e33NdQG6x8IEbMhlGFTGCjv5E0h27D/AbqCBU6PKrkGZowZHtKNtz7nITysa0hpyefM0rQkFHwf4NmR8uSHZ9YoT8VLStTtrF6daHSYlUT45EDXkiSA7NCAX8BIDbZNGxPGNCWR3reZxtQoJ89YPPdQngyqMn7sB1T4sP+euWpnAOOF5VDA2xyM2WtPzuKDZFD0XH5r3AqKf2jl+E1/jSBlhW1Y677WVLTLJm7+lMYYLK6YVaSsKQp10lOdhcNSoNF2iWUwmdCTfkrYIqM6Dz6FiYSrg1TTlOJb7Mg/z54eO6utHzIRFepBTL+pu9qP38Y21NtpUWu//jh0vpniXu1rzGQvuiuoNGIiqYA/dL39cpLB5JIwEVWNsnlHvzRcYQnqg2mhdkFMNZPhZwb3f+BV3GNA/dTziazHEgUN5G0esGl85PlZM7oLrPkXe1mYKh3pVRVHx9bScTQ/KR/Dll0NI1d5ul2SZa80Pmj29voelfMUa5Ml5q4zHzXK6N1E70ujMzWWJB025gLIb3e7aE9eN/5SJegL2ctob8uNQCTSouel6ko+Ykw/pstPL8IYWgsAwrckmzNJHasZi9MB9re639Zf+bDvflpigNboH6Jgzs7us5C2dozH1s5IzGP4XzgeALu8XP/T2coQxhj9xfaAJFrtqWRse9MM9gKIWCBEIzzIPeMa0+S7QAKAd7XRS/oZSDeqUFK2jRkrpbEnUxRErfX6lmHWlaY4CU8XKbSYU13LkuJcyzCm9EN/BU7f5cOwaVwSLMfOQeOx0GwJ5KRF4F30pVQIKfQmXt627TEmrutHr2mIgBLcte+S052JdumcRFMnlNLlmpl/rnOrU0bBeNDtXHQuYCWwfgiyZ47J2OZ+R68v0lA8mSdHFtJ81mbGnYQZn6PnZ/gbKz8uTu5WZE6CQ+n8IY6uvd8B0EYOjaoJK/FTQRjnYe6cBWTQFfmFzH441Ao3vcfKh6co4d5Ec1L9PXQWXidhCv4aJw1G06zgOTIcLCqvE3rbBpYOFUOmKlQbBmaIiKWqG+Xqk34jbQgzfHmcFO96sqh6B64/OzjPdjQupzCTM9hBNwSUABWoOAb9Yn0wtCUU5tRMTXf2DessFKbm5Jygvbh7KrBIQX3SGLQ1sHY+MYVbDpRTyi6/cATCCSWcekyT08mEh52KDKbrTR4CFdu2B7wsIqKxoiW4XiHOOc7DGtgVaCXgoce0nd9YXrtXbIjriMddx7wo/A5xvQbTOyaBuL/j3893Ny6eU10c18p8tZCzdBO1VOWH+R+8TkdvhkA995xzrdvjw7RoSdO7eqdyiOqkHBJGAp+OVSMWf0+/kHKRQf3LTT+CcwYN+7mx+Qz6Uw6Squ6/9JsxsEfIQ3VRsa84hUjcmmKf4QTpzy54RIteqPZc0kNQPobpKmvyWUUjldV0jgYi98XfErrdLQF3gyU/TG0kOsEPjuskIHoLEEka+nGXOjbtxj7HZe7onJ54kIluMkHeKfLWOMoZ2Qx9tqYMoGtbvLf6RrKh7METcaenirAS0waZfYWbIKS/XQQnlG0kWmK2VbUtKpKwAkjlEFEkYo8NtYRTqB1gYp7iyOhOcYT8Hdd6y+276P+sF+FuuXBAd1WJ8GWosOGcBOSMlvbxOyuaa58K10CUcBu3UNYMXfbifsNunrPPaJClyHK279gtweail/SVEc9XoyUh2uP/sL2eU3edeOBnscRj+0TXCkr+8auzW2BlMHrzDniQBXnFQelm94yDB4Ue3BfqKlkPmfC9QO+X/Jsjl6RtIQ6kqvwz9ZQ9uzFi4ZS5xd0I/IBp621GEXFkqIZhx13ohoiGa1KlhXS1AkDSRptzy7DlkW/ZDR3rh4WKGYsIF7/09WGtSPzicF8E3Mc9BS1OBksTuDuC1ayo6o0qxgEED0s3tNKE4GMQ5Kr9glfvoTbkMtgJst2R+Tq0/HozNCp5Dr4cqTcOZwOTtyPtZbdmzmdfT66mkdC4P1e8JPGugtHv+5HC8u0PSyBQGEewIo7g417mxC/5mf84Cp+1a/TLTe5mb4xqycaUexYM8+jLy4YdStpvR0NuTcx3ZzTwsi357S3NfACfLnU2BnD5WyvID9Y3uNd7Mawa4b+631WfuMdFWBOa3qQYifzdnzo98CpkpatCn5HSse87LqTxZ8Cm8ziu42g/uKcHuvGJXDzcFSD4wk0qclvpcdrSfhRZfU9H/YFK4JuW6q9aAvL24DgdD9mBDI2Gz94xFwu7u9iMivTQXEC5vgHD2lvdH9XtnpHxElqTBN4whFTujk9ZhjcEY50ntoaplQOs0N3S9XhQhLMjNz+g6CvY77jTq+3pdoGffuoecbD/hOWZqebdqaC5Oa7ytnH/YY2J6L3tCTaFloqNOetWvre3uaz5aNh4i6F4f4Z4CkTWGksHPcKdukMQDMNb0gm+JiWt9Wa2bHhQ1KmK+XQHZEIEM6GAhzIDCk0kMeKiyyE3zOwIJomqjHZVb5O4GmpJ1oONZzBSyvdBusaRYl1r8MT5BWgh0f8l34wc5pMxmvZUT11hZPDzawPfKLYCqHDAPvAEOR7JDkOBb3X8ILMRUIGslzzu17y0gP9qiOOuwadoyTcTHxvPJaEc6lQYn3j9E44C3TxDGpNOHl9rgT0EkRT4o2QNLNXXAoKPl2lFw/8goob9s73dMqtzeRwVVJdoC9kGv7TBzDJcs6yIXMM9EvExeuotANi99JdL4xLUZiRrtcv3+fJnIsJPdgczGYV/9ScODFIsa8N3fNFiIOe9b+5gPDwutGVYoSf2Fs+mzsbrxLv7/D+7vRAQxlya9WCvWtw8YjkTaBTEX+y8FUOHtHDBczu4XXEHaznZ1chnV/Pb5FkX78BiEO86ai1dB6v5+F2J/MLdEWhqEvEW8QYAPB6AYwfdEeVZX5igCF17mqbCqS/sg3z/hdW0iLSnjVeYqHXwTHbuFWM/c0MLCddnqvO49vZacWzXHk86wRyghbQTJUx3HUa/mJDGACZysvHLGzv2qjmlsKviokYLWA/Wsg2G1UV3eAmVq7VXt9s3v8nFvTRyqeWeHIkBpw45NFOR/H1sUjWAklBWOdiT/db0y6SOCvaTxVWqnk/6nL+/7vhhF3Zp9HrImt8+lk2vpeAS12YD8mHS/LJPWY+nLBWu1t0yI4aca70gJsBGJI5NvZbDGGe6f5SGfxbVHCr5NS/cBOUMOo+Oh87/IYv2XGZPwvD+7S9b84VnZVXdj9lQh0Et+v+zbaRkB4lquJMoGnF5UwcLtV0/RQybThfIpOYjNMs1LuW/6jPS6PCKlxeUGNwDvlKDCQlDhZkAQ1PPVPMlhA7QlMceaSYXnRLzI+IHJBQb6freSwWF6zBHYFI9H39CxxUUjIiR5sv5ViOi8tO5RgHtF3bsj3i8yQS43SdFTe0C4cxIuBuQMuSz7laE8/gZl3yGo7Wjgy/3RknSjla2sUt0d6PLQ2QQndxcWHKCNt19i6sYTttLqrywCHrjSyYwEBlWEC2nioEDONieaFc3iticbgSD/a5xzW9aefnQVFZWCRWkP0wVtBkf/tGtHl9PXau4+TV66rTq5X9aY3jEeU83OCnsuY5zBHFm3S5blMlvA+ebCcEWhyHDVwLfI7FAQ7fmcz3xO9cQf0waI6CK70yV+poRFAKDiCD1HELm6ij1KR/y2uWPF3LZ5P3pMGTr2W5hnC7Kvh+yqGvaID2L1athALmORgJHv1CDQzx0AzxfGMvUkgtQq8bCdjf0XvX1H4SKiPylc/2AwFM1PwS8eR429YnIMwJ6yyAQ68u5VyEGaFMdyrXX3I/vYStWcuUBrsN4oheWxIcNVKO1fFC0f6O0N5bRf8Kjyz9siRZK3z+vsU/zD1l6ShheQdoDeiXQKYgv91C7X3dJEx7fo60lRHVurySIUq1J5omLBMSl8b8F7Eqw1jTmZlVvSHcgzulhNYbv88MA1reVj6RQOfRA8LfaNID/RA89xAPYuEadd92uAjbQqExObqj5ELcNmhqOX/JrB+hn2aef3pWzSJmy98C9FdJuvF2l2XnZ5sDS25I9hnuCRAHeyHoAk4/VfjqEIjTN8BG1b0sTjSwJFBlHJCwBsKaaJviwXl5qynN1Z2f0IjoZ4gzwl9QDtcZDqYxk+qxbWDFqc9Nz/dhFLjGYTpX8VNEedNaa4/c/wTOpq7KioMEzASNlfwoIk9Ha6Xpph4PDoumFoeE6ONFEHNK3LgPDzEDfjj8HykTkq/VZIA7uN4BaaGFAi5Ah66FuVFFT7Mp9vUMvCfSH/Wdx/AYijS5WNBULlDpLJeLQ79oJfZ1ItrBOfD7oBSt5hIK7qoVM2aCMXwdOVSE6M34SJfgMPC9MCfcJudvQjDvYX31ZA42ZGGDgD4Iab75KMOXMdOR4aqlxCTb6FoLqqahzFTh6bMWtHEdqCz4uTVeHcH/FGUC/2j1BQE5FEKg3EIKl0uja5ztpSZa+3e59cmOHAPcyhlYE9ov+tU6GVzThHqBGs9Wd4zw6oNMvBt+YbbepNJotoU7YtuST1Z5mZIjpJaD+kDHqmmLyDYxlpWaHJFsPETkRsGj1Ko6lzgz1NYVMuejwOw2EwUtWQ+YKy7c3Y54dVk0hWD8oRaqOo8HNQ9YKURUjeIXXZ8OGst6WE5KtvoV/ga8VZQ8gE8SRc/XEWdXGrdDKc8tMxnVN03pSJQGIYquLQGU7QwUUftlJ6MYA8jgQFpGcW0FVBmsxl03zyxSL+FemGrtEoVwAOlfAiLaHuLV2QZ84j+dJAoWjdOFIF+uXwOzyeQNMPTwFg+ivSAkE9D23HFQJOnC34NYENFHBLCZfK+oICubzmXnFKHbLJrlNvlvbCONp+LiFK2iXMda3p8oGojI5o/92vlxF4mn5ZH4JZ8jRX6Xalj18Cw6H6j8UzO6w4cI5OV0RvFEKOwKmGvN/W7OvdHPMBMmg1oxFhAX6p8KdyULTdU+1C4JkX7mBND+Y5mjLv4/M3GIzyEcwyL6sxVTz0aqkKSCltyedUU/g9JmiuEp11d2Pk0skRo1o/gqPdJ5lbVG/fJXGcvxG6sIhAGcCOLIjHG873yWf2rJY7RRWk0W8lX5IjgvmDIWY9Tf8W8dNUWoRTbJ8IzAqL9YtYyOKDt4W8fdXKSRP+cljxQhmHmXTzyJ9mQkGpy0TWsf7VE96ki5aAhbRikjQ5mYN6xLyiLC1YKz8c/tZOb0bPH897bhFgjRHOtfzRbRm9p/Sqk4AMsStChqcVkZDM2bz3WOjNpBLI0/K8OCd+PDT+xg9ehjzBcM2WNjspKmch9rK6T58YRulEmbQbc9QT1+A2lPTnb2aXccptRrP+CwkQ2bsT/OMbUnE3NHvw0/G5Z3pSU0Qy6L10+lJ8SXzFGf4XTPoLuK+fNKj05IWFxJiHINtjo3WbwLdKZp8Kd97X+TW+z7XpP2qKqaRMfeYwzoc56y7w1NnOGzH48qicaC+DXipszT5mYmW6e1bxjwqNpo+E1JmXZQR/LeFi84XEGUYNxaaAciQfRX/6jO4b81TmdvWjyADWHwMonDXXHjO4YoSbh1M6pRKR2Psuw066/O/WvJh+WrIhlsJC6D9W2HtDK8gIU2F8SrpXOxd3hlntVFYsMo37s6kPAtFm2hu+eWN+NtX9/EO8psgAdiXvMNczCSFxV9cdEEXfDHHbQBmH/g+XilwtXYcjoZas9LqLS4xBSU4vLMnCqrtQ0qauh2g52wDFz6mAj7Z6OydXcVDwjK4QD2P2zfAYsfOqIhhg7xEzDJoumTy+eRbkGbFHraOet+LF5N8lKKplstYqiVamzJ56geEsZ6QP1NYv/KXdKk+OjtRC/KdoOZjrBXsoR8/CS8Az6JV/DyGqbTkwlo9gA5hnKaVKtRy9q7uNASl9asjj82WN/0VYIhMfLoR1CbW+vUrOSbAh/ilqbcq3HJ8c5+E2HuhKIbYRJRMQrfJrxKk7xKTRAgeSbA5dRzQACm31Ch2FSvoS5KnIpYXyqm4828lGGNApJiZ1aU0Q2mLUI/ZHAHrzMacshZtckhWG9n9MKIcDc8ld0gl7Tl5QFMOswgHvWyzuEqgTnuBxJqfhTprv6tbLJ4xcpNz2/v4fHE743YuVN5/lJG0SEfwUUq53MtXUqII9PRcjnTAN4TT3Y+cz6QWaW/l/tW0uSvniLha36gw3WlfA5qWdBuwE2uR0XMyYXE5d7WEkHUnAYzzJsRqrvhkqNwQO0V1jF14ApnUltMxqK3pbyWO3tw1o5+PIjcvKFeOFYCUucRa2lS0h4peA8FdiOcMkUabgHdtsAJuUrlxa+Vi39Y6PVg24XRyupLFIy80QmIF5H4GUY/aW5Ig687karDPyHVnr9M6PEJ3q3acV4yKwZSMBvlxqlvUFYgLm8NELucYtQvyI7CYRDl6LvSpdro+FK9IJSaOLXFghk90IlcJz4Ic74UY8a8+l+8tQKEjhVzFeYbLvrHGTv5A0OAs43fxIxAe5q/iqRFZUAXE+h0PUfky5af3xo/7zZXGhnzfz1Um8L58WUa5hTavUhbD0+zRqiajkJ7nyqIKj5b6nRTzHwG+lr7bxs5P5SPYycAoDdEqjT3TUqY3dz2tONxBh1lU7WX3BTxfO/WSqi0Zz+sEAOrvGu8io4SmIM4/Ka5RTXQscbG1I04g28x0fTm6KSXDFhAcNsNw0Mw8o2u5Lxa42szqPdapNqo6lrOP2N6RQ0pzOUJ6d/f/4Yqt1/r6XtoTqCPE70IwyZvaAWjqTX1XoLmvQ4uajXSpxDA84kZ1fFNd96Y892wh4ygX6RMg6vUd67kkRKcrmAckL8UmAklWAJryol61L8fPBt8o32hDuVCE2itZBjVn/4vEZVMARe0U7X/NOb6NDdrbP8TCM4g4+XeV7Jej+MnqvdA0c19fSUqxU1fhrSQL4cuNcdPKbdPSyPzptcmrxngxL9Yotv6/Ah2c4kL8dt1m7uQE8cdunn3qMB176itdJQu6lEF3p/gg/kuBO70VtY6gVISN1cNPnv8EiKYKzLhCf89np5f/fCDWOhciRKDwWRzULf3jZvHDJ8cd25p5JA3UppQIqy/8C9g6UrJwmYWZ8f8z0+sGCY25gGwsQs5UREaq9GQKE//vFneh09XAVdDhzDkqspkWjL6RmLDWdIpttc0N5NTdX2DdqR9JVikvEkg9OBHpYxkxYr0RQMp5bphj0ee6KsYcbZ4x06gbJFeGSgKZjuR4EpDJ8yPJXFFxldxmUtsrCMr/z9NA9olxU7+l4OD+dXd0AEAfMtpIK+g98cuLM0HNfslZ5/YgWBxFF2zmKJFunA9bb/agMyUR9AGAVr/HER9lxJEwdbwKtHQy9vwiMF8G+452JLK/t0/j0mvspENFA9z2ZJPL9jFbSRuF34ppQ3mV/E14IZeGP29NIv2uOHOxL1uzKwNydi1YbLIJRDE25zm0Zh/ulUBJWEnMqUfmzDqo1V8qy4VxfXeym4xccedSOiEyyLna/lP08giG6hUmQLxnvzrMHK2AEe+ggm3RGoS8Rown66LX+See1iohZqnbqxISRf6rNBpM45XPvxph3Q3pKcjDvoeq9eaUFSA92VfxpnVBO/tXtiQGBLYuBSZpqxTWd6G4RP/PFdrmUOhVkRsUXBufoj2MKSgNx6Ab+KMUg5qY52yrkYGKt8O6CubJt0/GJnIlCRODd3ZdcuCNaVWx2PN5+LK6Jy4jEFKtw/f+Ub+piXcpBL02TiO1SCI7qU0MznFxbs5CYrYOo5uQe/CaxWIN8Pq57WuPJ4UT+FHcRW6eiVYxa5hQTAf/ai3XsH5xP2LT/CWdWB175xA8uN8iZ6ai3yJysIENsHcRD6bpGEZ6gc+IIFu+JEhm4zMxIhu9CqjscKnyUIIzasUxdo2Ioa6hZaq4exO0dC/5gQbL4vR23nf5/PfxEzggWNQyDhM+UupTXtBIO2yqV6/wotUPibw5rOcqU+Amua0hAt+7RVXHwvfnFVfCo8E6mdD8wv8xSK2i1mElL9KiW/eVZWBRNE8gSBYQ8DpaNF193CPDyWKxPHz3Hg/aehKa5P4GPE7ugkqhjsYVMUo9Pvq7wXrjAs6U8EKDEh9zsRfFyg91RJaRSqSMNaUbuTkqKYq7KalV3WErKQRL6SxCZa3xGwJ8H5t5bYU+J3lCbtQjtDZ8O66PwKPPvyx1zuiiMjQcwv+JNGNIlFraoEgz5MlGkrBEgIRGXZDuS6ZVAa8L8lqE+/en4pHcj6HNlKtriDeosSDduttT+zi45k4bkgXWgO0a8d7gdqZ3UJR29T2KFIOSrm2Eqat9SF4S8lc33+LUUePfo82x/nQilk3+Eu+HhupyjfM5PGq1dglQ4wG/lJYazW0LlJ774pf/fwHYuPGjVC0CgsOjDzN8o2F6koQV+o90KCDPNkmqpGuAx8+43UGbtxxZGt0OpiFwi3tp8JyYExjdfni19OpI6SI48mM4pZCQXpkhvbZnbvD9hHigF+ERZJfzZZRGExPctz5QL8eQNAFI/KuL4DC23teZmSi1RNxU26PZTrfmtXwgciXXMyvm8j2ACq5n3+x4PyKdvh+SAVEvOQpc1SO7JDZf4+1L+fmB31RWMENRock5xOJ6H2UdMncn+nU9plECElvoLRnImET+mfWHT1fAfrR6iA1Yks9UXu3F2KHIc/wAeChopbz8RCkHQGxOQEVSbujBdd/Hjod1X8g4kF4MsefT+gd4n0UGoUgd30zeGmabC7h7W29PhizM8IZvfZbzVFdLPLU5X8s+wmdN65Woyld4B+BVEQ+w1GA4J4K6WVcSki7AeL4Gv6v3DXYBgdHl5aHXI1epDe0MxtdxvyPhHxoO3pHodmCkdJ1udrGu1167EDHXzknCB5r5U9cxI/UFt54Xxr5LPwrfONSxhMj3DqfGLSQQfxf2cSWWFig3jGf2mOfQ+XzH62mfn3Dv0KSS2M12rilop/c5iFK5ApaiFse2g==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData></saml2:EncryptedAssertion></saml2p:Response>
diff --git a/ozg-antragsraum-server/src/test/resources/testfiles/TestDaten.zip b/server/src/test/resources/testfiles/TestDaten.zip
similarity index 100%
rename from ozg-antragsraum-server/src/test/resources/testfiles/TestDaten.zip
rename to server/src/test/resources/testfiles/TestDaten.zip
diff --git a/ozg-antragsraum-server/src/test/resources/testfiles/TestDokument.docx b/server/src/test/resources/testfiles/TestDokument.docx
similarity index 100%
rename from ozg-antragsraum-server/src/test/resources/testfiles/TestDokument.docx
rename to server/src/test/resources/testfiles/TestDokument.docx
diff --git a/ozg-antragsraum-server/src/test/resources/testfiles/TestDokument.odt b/server/src/test/resources/testfiles/TestDokument.odt
similarity index 100%
rename from ozg-antragsraum-server/src/test/resources/testfiles/TestDokument.odt
rename to server/src/test/resources/testfiles/TestDokument.odt
diff --git a/ozg-antragsraum-server/src/test/resources/testfiles/TestDokument.pdf b/server/src/test/resources/testfiles/TestDokument.pdf
similarity index 100%
rename from ozg-antragsraum-server/src/test/resources/testfiles/TestDokument.pdf
rename to server/src/test/resources/testfiles/TestDokument.pdf
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index f15d7a4ab8106ec98508fe36bfce651585cc2c59..0000000000000000000000000000000000000000
--- a/settings.gradle
+++ /dev/null
@@ -1,4 +0,0 @@
-rootProject.name = "ozg-antragsraum"
-include 'keycloak', 'ozg-antragsraum-server'
-project(":keycloak").name = "ozg-keycloak"
-project(":ozg-antragsraum-server").name = "ozg-antragsraum-server"
\ No newline at end of file
diff --git a/ozg-antragsraum-server/src/test/resources/testcontainers.properties b/sonar-project.properties
old mode 100755
new mode 100644
similarity index 70%
rename from ozg-antragsraum-server/src/test/resources/testcontainers.properties
rename to sonar-project.properties
index 19c08aa975f71f3cff698c32f020d2ec8b4c9a6f..14f9a5bd8faef0dc24a67746b1fc22dd0e51abf3
--- a/ozg-antragsraum-server/src/test/resources/testcontainers.properties
+++ b/sonar-project.properties
@@ -1,21 +1,28 @@
-#
-# 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.
-#
-hub.image.name.prefix=docker-repos.dockerregistry.mgm-tp.com/
\ No newline at end of file
+#
+# 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.
+#
+sonar.java.coveragePlugin=jacoco
+sonar.jacoco.reportPath=target/jacoco.exec
+sonar.projectKey=antragsraum-server
+sonar.sources=server/src
+sonar.tests=server/src
+sonar.test.inclusions=**/*.java
+sonar.exclusions=**/target/**
+sonar.sourceEncoding=UTF-8
+sonar.projectName=Antragsraum Server