diff --git a/.gitignore b/.gitignore
index 549e00a2a96fa9d7c5dbc9859664a78d980158c2..9ddbf280ee467a8bf60fb940d990634d1f0cbabf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,5 @@ build/
 
 ### VS Code ###
 .vscode/
+
+bin/**
diff --git a/Jenkinsfile b/Jenkinsfile
index ce7f72860a1c5fb91d5b0672ef59500f6c294b31..b11447de08cd0645e6f427af44fff71823e7e53d 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -10,8 +10,8 @@ pipeline {
         SNAPSHOT_REGEX = /\d+.\d+.\d+-SNAPSHOT/
         FAILED_STAGE = ""
         SH_SUCCESS_STATUS_CODE = 0
-        IMAGE_TAG = generateImageTag()
-        HELM_CHART_VERSION = generateHelmChartVersion()
+        IMAGE_TAG = buildVersionName()
+        HELM_CHART_VERSION = buildVersionName()
         BUILD_PROFILE = getBuildProfile()
     }
 
@@ -44,7 +44,7 @@ pipeline {
           when {
             not {
                 anyOf {
-                    branch 'master'
+                    branch 'main'
                     branch 'release'
                 }
             }
@@ -56,10 +56,10 @@ pipeline {
                 }
                 configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
                     sh "mvn -s $MAVEN_SETTINGS versions:set -DnewVersion=${JAR_TAG} -DprocessAllModules=true"
-                    
+
                 }
           }
-        }   
+        }
 
         stage('Build Administration') {
             steps {
@@ -72,7 +72,7 @@ pipeline {
                 }
             }
         }
-        
+
         stage('Deploy to Nexus'){
             steps {
                 script {
@@ -87,7 +87,7 @@ pipeline {
 
         stage('Sonar Checks') {
             when {
-                branch 'master'
+                branch 'main'
             }
             steps {
                 script {
@@ -106,42 +106,26 @@ pipeline {
             }
         }
 
-        stage ('Deploy SBOM to DependencyTrack') {
-            steps {
-                script {
-                    IMAGE_TAG = generateImageTag()
-
-                    configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
-                        withCredentials([string(credentialsId: 'dependency-track-api-key', variable: 'API_KEY')]) {
-
-                            catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
-                                sh "mvn  --no-transfer-progress -s $MAVEN_SETTINGS io.github.pmckeown:dependency-track-maven-plugin:upload-bom -Ddependency-track.apiKey=$API_KEY -Ddependency-track.projectVersion=${IMAGE_TAG} -Ddependency-track.dependencyTrackBaseUrl=https://dependency-track.ozg-sh.de"
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
         stage('Build and publish Docker image') {
             steps {
                 script {
+					IMAGE_TAG = buildVersionName()
                     FAILED_STAGE=env.STAGE_NAME
                 }
-                
+
                 configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
                     withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
-                        sh 'mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD} -Dbuild.number=$BUILD_NUMBER -DimageTag=$IMAGE_TAG -DpublishImage=true'
+                        sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS -Dspring-boot.build-image.publish=true -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD} -Dbuild.number=$BUILD_NUMBER spring-boot:build-image -Dspring-boot.build-image.imageName=docker.ozg-sh.de/administration:${IMAGE_TAG}'
                     }
                 }
             }
         }
-        
+
         stage('Test, build and deploy Helm Chart') {
             steps {
                 script {
                     FAILED_STAGE=env.STAGE_NAME
-                    HELM_CHART_VERSION = generateHelmChartVersion()
+                    HELM_CHART_VERSION = buildVersionName()
 
                     dir('src/main/helm') {
                         sh "helm lint -f ../../test/helm-linter-values.yaml"
@@ -158,7 +142,7 @@ pipeline {
 
         stage('Trigger Dev rollout') {
             when {
-                branch 'master'
+                branch 'main'
             }
             steps {
                 script {
@@ -189,15 +173,32 @@ pipeline {
                 }
             }
         }
+        
+         stage ('Deploy SBOM to DependencyTrack') {
+            steps {
+                script {
+                    IMAGE_TAG_WO_COMMIT = buildVersionNameWithoutCommidId()
+
+                    configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
+                        withCredentials([string(credentialsId: 'dependency-track-api-key', variable: 'API_KEY')]) {
+
+                            catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
+                                sh "mvn  --no-transfer-progress -s $MAVEN_SETTINGS io.github.pmckeown:dependency-track-maven-plugin:upload-bom -Ddependency-track.apiKey=$API_KEY -Ddependency-track.projectVersion=${IMAGE_TAG_WO_COMMIT} -Ddependency-track.dependencyTrackBaseUrl=https://dependency-track.ozg-sh.de"
+                            }
+                        }
+                    }
+                }
+            }
+        }
     }
-    
+
     post {
         always{
             junit testResults: '**/target/surefire-reports/*.xml', skipPublishingChecks: true
         }
         failure {
             script {
-                if (isMasterBranch() || isReleaseBranch()) {
+                if (isMainBranch() || isReleaseBranch()) {
                     sendFailureMessage()
                 }
             }
@@ -224,8 +225,28 @@ String getHelmRepository(){
     return 'ozg-base-apps-snapshot';
 }
 
-String generateImageTag() {
-    return "${env.BRANCH_NAME}-${getPomVersion()}"
+String validateBranchName(branchName) {
+    int maxLength = 20
+    if (branchName.length() > maxLength) {
+        String originalBranchName = branchName
+        branchName = branchName.substring(0, maxLength)
+        echo "WARNING: Branch name '${originalBranchName}' exceeded ${maxLength} characters. " +
+             "It has been truncated to '${branchName}' for deployment purposes."
+    }
+    return branchName
+}
+
+String buildVersionName() {
+    if (isReleaseBranch()) {
+        return getPomVersion()
+    }
+    return "${getPomVersion()}-${validateBranchName(env.BRANCH_NAME)}-${env.GIT_COMMIT.take(7)}".replaceAll("_", "-")
+}
+String buildVersionNameWithoutCommidId() {
+	if (isReleaseBranch()) {
+        return getPomVersion()
+    }
+    return "${getPomVersion()}-${validateBranchName(env.BRANCH_NAME)}".replaceAll("_", "-")
 }
 
 String getPomVersion() {
@@ -235,7 +256,7 @@ String getPomVersion() {
 }
 
 String getBuildProfile() {
-    if (isMasterBranch()) {
+    if (isMainBranch()) {
         return "-P dev"
     } else if (isReleaseBranch()) {
         return "-P release"
@@ -270,7 +291,7 @@ String getRoom() {
         return "!oWZpUGTFsxkJIYNfYg:matrix.ozg-sh.de"
     } else {
         return "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de"
-    } 
+    }
 }
 
 Void configureGit() {
@@ -340,23 +361,10 @@ Boolean hasAdministrationValuesFileChanged(String environment) {
     return sh (script: "git status | grep '${environment}/application/values/administration-values.yaml'", returnStatus: true) == env.SH_SUCCESS_STATUS_CODE as Integer
 }
 
-Boolean isMasterBranch() {
-    return env.BRANCH_NAME == 'master'
+Boolean isMainBranch() {
+    return env.BRANCH_NAME == 'main'
 }
 
 Boolean isReleaseBranch() {
     return env.BRANCH_NAME == 'release'
 }
-
-String generateHelmChartVersion() {
-    def chartVersion = getPomVersion()
-
-    if (isMasterBranch()) {
-        chartVersion += "-${env.GIT_COMMIT.take(7)}"
-    }
-    else if (!isReleaseBranch()) {
-        chartVersion += "-${env.BRANCH_NAME}"
-    }
-
-    return chartVersion.replaceAll("_", "-").take(63 - "administration-".length())
-}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..1a7270cb490ecb7ed8e475b8cee2e5069b9f7b12
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,101 @@
+OPEN-SOURCE-LIZENZ FÜR DIE EUROPÄISCHE UNION v. 1.2 
+EUPL © Europäische Union 2007, 2016 
+Diese Open-Source-Lizenz für die Europäische Union („EUPL“) gilt für Werke (im Sinne der nachfolgenden Begriffsbestimmung), die unter EUPL-Bedingungen zur Verfügung gestellt werden. Das Werk darf nur in der durch diese Lizenz gestatteten Form genutzt werden (insoweit eine solche Nutzung dem Urheber vorbehalten ist). 
+Das Werk wird unter den Bedingungen dieser Lizenz zur Verfügung gestellt, wenn der Lizenzgeber (im Sinne der nachfolgenden Begriffsbestimmung) den folgenden Hinweis unmittelbar hinter dem Urheberrechtshinweis dieses Werks anbringt: 
+                      Lizenziert unter der EUPL 
+oder in einer anderen Form zum Ausdruck bringt, dass er es unter der EUPL lizenzieren möchte. 
+
+1.Begriffsbestimmungen 
+Für diese Lizenz gelten folgende Begriffsbestimmungen:  
+— „Lizenz“:diese Lizenz.  
+— „Originalwerk“:das Werk oder die Software, die vom Lizenzgeber unter dieser Lizenz verbreitet oder zugänglich gemacht wird, und zwar als Quellcode und gegebenenfalls auch als ausführbarer Code.  
+— „Bearbeitungen“:die Werke oder Software, die der Lizenznehmer auf der Grundlage des Originalwerks oder seiner Bearbeitungen schaffen kann. In dieser Lizenz wird nicht festgelegt, wie umfangreich die Änderung oder wie stark die Abhängigkeit vom Originalwerk für eine Einstufung als Bearbeitung sein muss; dies bestimmt sich nach dem Urheberrecht, das in dem unter Artikel 15 aufgeführten Land anwendbar ist.  
+— „Werk“:das Originalwerk oder seine Bearbeitungen.  
+— „Quellcode“:diejenige Form des Werkes, die zur Auffassung durch den Menschen bestimmt ist und die am besten geeignet ist, um vom Menschen verstanden und verändert zu werden.  
+— „Ausführbarer Code“:die — üblicherweise — kompilierte Form des Werks, die von einem Computer als Programm ausgeführt werden soll.  
+— „Lizenzgeber“:die natürliche oder juristische Person, die das Werk unter der Lizenz verbreitet oder zugänglich macht.  
+— „Bearbeiter“:jede natürliche oder juristische Person, die das Werk unter der Lizenz verändert oder auf andere Weise zur Schaffung einer Bearbeitung beiträgt.  
+— „Lizenznehmer“ („Sie“):jede natürliche oder juristische Person, die das Werk unter den Lizenzbedingungen nutzt.  
+— „Verbreitung“ oder „Zugänglichmachung“:alle Formen von Verkauf, Überlassung, Verleih, Vermietung, Verbreitung, Weitergabe, Übermittlung oder anderweitiger Online- oder Offline-Bereitstellung von Vervielfältigungen des Werks oder Zugänglichmachung seiner wesentlichen Funktionen für dritte natürliche oder juristische Personen.
+
+2.Umfang der Lizenzrechte 
+Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine weltweite, unentgeltliche, nicht ausschließliche, unterlizenzierbare Lizenz, die Sie berechtigt: 
+—  das Werk uneingeschränkt zu nutzen, 
+—  das Werk zu vervielfältigen, 
+—  das Werk zu verändern und Bearbeitungen auf der Grundlage des Werks zu schaffen, 
+—  das Werk öffentlich zugänglich zu machen, was das Recht einschließt, das Werk oder Vervielfältigungsstücke davon öffentlich bereitzustellen oder wahrnehmbar zu machen oder das Werk, soweit möglich, öffentlich aufzuführen, 
+—  das Werk oder Vervielfältigungen davon zu verbreiten, 
+—  das Werk oder Vervielfältigungen davon zu vermieten oder zu verleihen, 
+—  das Werk oder Vervielfältigungen davon weiter zu lizenzieren. 
+Für die Wahrnehmung dieser Rechte können beliebige, derzeit bekannte oder künftige Medien, Träger und Formate verwendet werden, soweit das geltende Recht dem nicht entgegensteht. Für die Länder, in denen Urheberpersönlichkeitsrechte an dem Werk bestehen, verzichtet der Lizenzgeber im gesetzlich zulässigen Umfang auf seine Urheberpersönlichkeitsrechte, um die Lizenzierung der oben aufgeführten Verwertungsrechte wirksam durchführen zu können. Der Lizenzgeber erteilt dem Lizenznehmer ein nicht ausschließliches, unentgeltliches Nutzungsrecht an seinen Patenten, sofern dies zur Ausübung der durch die Lizenz erteilten Nutzungsrechte am Werk notwendig ist. 
+
+3.Zugänglichmachung des Quellcodes 
+Der Lizenzgeber kann das Werk entweder als Quellcode oder als ausführbaren Code zur Verfügung stellen. Stellt er es als ausführbaren Code zur Verfügung, so stellt er darüber hinaus eine maschinenlesbare Kopie des Quellcodes für jedes von ihm verbreitete Vervielfältigungsstück des Werks zur Verfügung, oder er verweist in einem Vermerk im Anschluss an den dem Werk beigefügten Urheberrechtshinweis auf einen Speicherort, an dem problemlos und unentgeltlich auf den Quellcode zugegriffen werden kann, solange der Lizenzgeber das Werk verbreitet oder zugänglich macht. 
+
+4.Einschränkungen des Urheberrechts
+Es ist nicht Zweck dieser Lizenz, Ausnahmen oder Schranken der ausschließlichen Rechte des Urhebers am Werk, die dem Lizenznehmer zugutekommen, einzuschränken. Auch die Erschöpfung dieser Rechte bleibt von dieser Lizenz unberührt. 
+
+5.Pflichten des Lizenznehmers 
+Die Einräumung der oben genannten Rechte ist an mehrere Beschränkungen und Pflichten für den Lizenznehmer gebunden: 
+
+Urheberrechtshinweis, Lizenztext, Nennung des Bearbeiters: Der Lizenznehmer muss alle Urheberrechts-, Patent- oder Markenrechtshinweise und alle Hinweise auf die Lizenz und den Haftungsausschluss unverändert lassen. Jedem von ihm verbreiteten oder zugänglich gemachten Vervielfältigungsstück des Werks muss der Lizenznehmer diese Hinweise sowie diese Lizenz beifügen. Der Lizenznehmer muss auf jedem abgeleiteten Werk deutlich darauf hinweisen, dass das Werk geändert wurde, und das Datum der Bearbeitung angeben.
+
+„Copyleft“-Klausel: Der Lizenznehmer darf Vervielfältigungen des Originalwerks oder Bearbeitungen nur unter den Bedingungen dieser EUPL oder einer neueren Version dieser Lizenz verbreiten oder zugänglich machen, außer wenn das Originalwerk ausdrücklich nur unter dieser Lizenzversion — z. B. mit der Angabe „Nur EUPL V. 1.2“ — verbreitet werden darf. Der Lizenznehmer (der zum Lizenzgeber wird) darf für das Werk oder die Bearbeitung keine zusätzlichen Bedingungen anbieten oder vorschreiben, die die Bedingungen dieser Lizenz verändern oder einschränken. 
+
+Kompatibilitäts-Klausel: Wenn der Lizenznehmer Bearbeitungen, die auf dem Werk und einem anderen Werk, das unter einer kompatiblen Lizenz lizenziert wurde, basieren, oder die Kopien dieser Bearbeitungen verbreitet oder zugänglich macht, kann dies unter den Bedingungen dieser kompatiblen Lizenz erfolgen. Unter „kompatibler Lizenz“ ist eine im Anhang dieser Lizenz angeführte Lizenz zu verstehen. Sollten die Verpflichtungen des Lizenznehmers aus der kompatiblen Lizenz mit denjenigen aus der vorliegenden Lizenz (EUPL) in Konflikt stehen, werden die Verpflichtungen aus der kompatiblen Lizenz Vorrang haben.
+
+Bereitstellung des Quellcodes: Wenn der Lizenznehmer Vervielfältigungsstücke des Werks verbreitet oder zugänglich macht, muss er eine maschinenlesbare Fassung des Quellcodes mitliefern oder einen Speicherort angeben, über den problemlos und unentgeltlich so lange auf diesen Quellcode zugegriffen werden kann, wie der Lizenznehmer das Werk verbreitet oder zugänglich macht. 
+
+Rechtsschutz: Diese Lizenz erlaubt nicht die Benutzung von Kennzeichen, Marken oder geschützten Namensrechten des Lizenzgebers, soweit dies nicht für die angemessene und übliche Beschreibung der Herkunft des Werks und der inhaltlichen Wiedergabe des Urheberrechtshinweises erforderlich ist.
+
+6.Urheber und Bearbeiter 
+Der ursprüngliche Lizenzgeber gewährleistet, dass er das Urheberrecht am Originalwerk innehat oder dieses an ihn lizenziert wurde und dass er befugt ist, diese Lizenz zu erteilen. 
+Jeder Bearbeiter gewährleistet, dass er das Urheberrecht an den von ihm vorgenommenen Änderungen des Werks besitzt und befugt ist, diese Lizenz zu erteilen. 
+Jedes Mal, wenn Sie die Lizenz annehmen, erteilen Ihnen der ursprüngliche Lizenzgeber und alle folgenden Bearbeiter eine Befugnis zur Nutzung ihrer Beiträge zum Werk unter den Bedingungen dieser Lizenz. 
+
+7.Gewährleistungsausschluss 
+Die Arbeit an diesem Werk wird laufend fortgeführt; es wird durch unzählige Bearbeiter ständig verbessert. Das Werk ist nicht vollendet und kann daher Fehler („bugs“) enthalten, die dieser Art der Entwicklung inhärent sind. 
+Aus den genannten Gründen wird das Werk unter dieser Lizenz „so, wie es ist“ ohne jegliche Gewährleistung zur Verfügung gestellt. Dies gilt unter anderem — aber nicht ausschließlich — für Marktreife, Verwendbarkeit für einen bestimmten Zweck, Mängelfreiheit, Richtigkeit sowie Nichtverletzung von anderen Immaterialgüterrechten als dem Urheberrecht (vgl. dazu Artikel 6 dieser Lizenz). 
+Dieser Gewährleistungsausschluss ist wesentlicher Bestandteil der Lizenz und Bedingung für die Einräumung von Rechten an dem Werk.
+
+8.Haftungsausschluss/Haftungsbeschränkung 
+Außer in Fällen von Vorsatz oder der Verursachung von Personenschäden haftet der Lizenzgeber nicht für direkte oder indirekte, materielle oder immaterielle Schäden irgendwelcher Art, die aus der Lizenz oder der Benutzung des Werks folgen; dies gilt unter anderem, aber nicht ausschließlich, für Firmenwertverluste, Produktionsausfall, Computerausfall oder Computerfehler, Datenverlust oder wirtschaftliche Schäden, und zwar auch dann, wenn der Lizenzgeber auf die Möglichkeit solcher Schäden hingewiesen wurde. Unabhängig davon haftet der Lizenzgeber im Rahmen der gesetzlichen Produkthaftung, soweit die entsprechenden Regelungen auf das Werk anwendbar sind. 
+
+9.Zusatzvereinbarungen 
+Wenn Sie das Werk verbreiten, können Sie Zusatzvereinbarungen schließen, in denen Verpflichtungen oder Dienstleistungen festgelegt werden, die mit dieser Lizenz vereinbar sind. Sie dürfen Verpflichtungen indessen nur in Ihrem eigenen Namen und auf Ihre eigene Verantwortung eingehen, nicht jedoch im Namen des ursprünglichen Lizenzgebers oder eines anderen Bearbeiters, und nur, wenn Sie sich gegenüber allen Bearbeitern verpflichten, sie zu entschädigen, zu verteidigen und von der Haftung freizustellen, falls aufgrund der von Ihnen eingegangenen Gewährleistungsverpflichtung oder Haftungsübernahme Forderungen gegen sie geltend gemacht werden oder eine Haftungsverpflichtung entsteht. 
+
+10.Annahme der Lizenz 
+Sie können den Bestimmungen dieser Lizenz zustimmen, indem Sie das Symbol „Lizenz annehmen“ unter dem Fenster mit dem Lizenztext anklicken oder indem Sie Ihre Zustimmung auf vergleichbare Weise in einer nach anwendbarem Recht zulässigen Form geben. Das Anklicken des Symbols gilt als Anzeichen Ihrer eindeutigen und unwiderruflichen Annahme der Lizenz und der darin enthaltenen Klauseln und Bedingungen. In gleicher Weise gilt als Zeichen der eindeutigen und unwiderruflichen Zustimmung die Ausübung eines Rechtes, das in Artikel 2 dieser Lizenz angeführt ist, wie das Erstellen einer Bearbeitung oder die Verbreitung oder Zugänglichmachung des Werks oder dessen Vervielfältigungen. 
+
+11.Informationspflichten
+Wenn Sie das Werk verbreiten oder zugänglich machen (beispielsweise, indem Sie es zum Herunterladen von einer Website anbieten), müssen Sie über den Vertriebskanal oder das benutzte Verbreitungsmedium der Öffentlichkeit zumindest jene Informationen bereitstellen, die nach dem anwendbaren Recht bezüglich der Lizenzgeber, der Lizenz und ihrer Zugänglichkeit, des Abschlusses des Lizenzvertrags sowie darüber, wie die Lizenz durch den Lizenznehmer gespeichert und vervielfältigt werden kann, erforderlich sind.
+
+12.Beendigung der Lizenz 
+Die Lizenz und die damit eingeräumten Rechte erlöschen automatisch, wenn der Lizenznehmer gegen die Lizenzbedingungen verstößt. Ein solches Erlöschen der Lizenz führt nicht zum Erlöschen der Lizenzen von Personen, denen das Werk vom Lizenznehmer unter dieser Lizenz zur Verfügung gestellt worden ist, solange diese Personen die Lizenzbedingungen erfüllen. 
+13.Sonstiges
+Unbeschadet des Artikels 9 stellt die Lizenz die vollständige Vereinbarung der Parteien über das Werk dar. Sind einzelne Bestimmungen der Lizenz nach geltendem Recht nichtig oder unwirksam, so berührt dies nicht die Wirksamkeit oder Durchsetzbarkeit der Lizenz an sich. Solche Bestimmungen werden vielmehr so ausgelegt oder modifiziert, dass sie wirksam und durchsetzbar sind. Die Europäische Kommission kann weitere Sprachfassungen oder neue Versionen dieser Lizenz oder aktualisierte Fassungen des Anhangs veröffentlichen, soweit dies notwendig und angemessen ist, ohne den Umfang der Lizenzrechte zu verringern. Neue Versionen werden mit einer eindeutigen Versionsnummer veröffentlicht. Alle von der Europäischen Kommission anerkannten Sprachfassungen dieser Lizenz sind gleichwertig. Die Parteien können sich auf die Sprachfassung ihrer Wahl berufen.
+
+14.Gerichtsstand
+Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: 
+—  Für alle Streitigkeiten über die Auslegung dieser Lizenz zwischen den Organen, Einrichtungen und sonstigen Stellen der Europäischen Union als Lizenzgeber und einem Lizenznehmer ist der Gerichtshof der Europäischen Union gemäß Artikel 272 des Vertrags über die Arbeitsweise der Europäischen Union zuständig; 
+—  Gerichtsstand für Streitigkeiten zwischen anderen Parteien über die Auslegung dieser Lizenz ist allein der Ort, an dem der Lizenzgeber seinen Wohnsitz oder den wirtschaftlichen Mittelpunkt seiner Tätigkeit hat. 
+
+15.Anwendbares Recht
+Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: 
+—  Diese Lizenz unterliegt dem Recht des Mitgliedstaats der Europäischen Union, in dem der Lizenzgeber seinen Sitz, Wohnsitz oder eingetragenen Sitz hat; 
+—  diese Lizenz unterliegt dem belgischen Recht, wenn der Lizenzgeber keinen Sitz, Wohnsitz oder eingetragenen Sitz in einem Mitgliedstaat der Europäischen Union hat.
+   
+Anlage 
+„Kompatible Lizenzen“ nach Artikel 5 der EUPL sind: 
+—  GNU General Public License (GPL) v. 2, v. 3 
+—  GNU Affero General Public License (AGPL) v. 3 
+—  Open Software License (OSL) v. 2.1, v. 3.0 
+—  Eclipse Public License (EPL) v. 1.0 
+—  CeCILL v. 2.0, v. 2.1 
+—  Mozilla Public Licence (MPL) v. 2 
+—  GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 
+—  Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) für andere Werke als Software 
+—  European Union Public Licence (EUPL) v. 1.1, v. 1.2 
+—  Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) oder Strong Reciprocity (LiLiQ-R+)
+Die Europäische Kommission kann diesen Anhang aktualisieren, um neuere Fassungen der obigen Lizenzen aufzunehmen, ohne hierfür eine neue Fassung der EUPL auszuarbeiten, solange diese Lizenzen die in Artikel 2 gewährten Rechte gewährleisten und den erfassten Quellcode vor ausschließlicher Aneignung schützen.
+Alle sonstigen Änderungen oder Ergänzungen dieses Anhangs bedürfen der Ausarbeitung einer neuen Version der EUPL.    
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e3822b358f647ddbe6db35957b929ea29326cc0d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+# Administration
+Die Anwendung Administration der OZG-Cloud stellt ein Web-UI zur Verwaltung der globalen Einstellungen der OZG-Cloud zur Verfügung. Über dieses erfolgt die fachliche Administration der OZG-Cloud durch den Mandanten oder eine durch den Mandanten beauftragte Institution.
+
+## Architektur
+ 
+### Client
+
+Der Client ist mit Angular entwickelt und bindet sich per REST-API an das Backend. Der Client wird im mono-repo der OZG-Cloud Clients mit entwickelt und ist ebenfalls auf [code.schleswig-holstein.de](https://code.schleswig-holstein.de/ozg-cloud/app/frontend-clients/-/tree/main/alfa-client/apps/admin?ref_type=heads) zu finden.
+
+### Backend
+
+Das Backend (in diesem Repo zu finden) ist eine spring-boot basierte Java Anwendung. 
+
+* spring data rest - das Rest Api für den Client wird per spring data rest aufgebaut.
+* spring cloud server - zur Übertragung der Konfigurationen an die einzelnen Dienste der OZG-Cloud (im Kontext der OZG-Cloud 'Manager' genannt) wird eine Schnittstelle nach dem spring cloud server Standard bereit gestellt.
+
+Die Konfiguration wird in einer MongoDB gespeichert.
+
+## Entwicklung
+
+Um ein neue Konfiguration einzubauen, müssen folgende Klassen angelegt werden:
+
+* Datenklassen - Die Klassen mit den eigentlichen Konfigurationsparameter. Die Klasse wird nach der Fachlichkeit benannt. Diese wird mit @TypeAlias annotiert.
+* Repository - MongoRepository mit @RepositoryRestResource annotation. Die findAll Methode muss überschrieben werden.
+* Klasse als Datentransferobjekt (DTO) - Format für die Spring Config Server Schnittstelle für einen Manager. Bennenung daher: FachlichkeitManagerSettingDto.java
+* Mapper - Mapped die Konfigurationsparamter in das Dto.
+* DtoService - Stellt die Settings für einen MAnager bereit. Wird mit @DtoService annotiert. Dadurch ist es eine Spring-Bean und wird automatisch aufgerufen, wenn eine Konfiguration für den jeweiligen Manager geladen wird. Benennung: FachlichkeitManagerSettingDtoService
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 41cb77e595deecc3258ef867568667cb47a5c9b5..cbbc05e062d7cf73ca49e70fd4ec0be52c259574 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,12 +31,13 @@
 	<parent>
 		<groupId>de.ozgcloud.common</groupId>
 		<artifactId>ozgcloud-common-parent</artifactId>
-		<version>4.6.0</version>
-		<relativePath/>
+		<version>4.9.0-SNAPSHOT</version>
+		<relativePath />
 	</parent>
+
 	<groupId>de.ozgcloud</groupId>
 	<artifactId>administration</artifactId>
-	<version>1.2.1</version>
+	<version>1.3.0-SNAPSHOT</version>
 	<name>Administration</name>
 	<description>Administration Backend Project</description>
 	<inceptionYear>2024</inceptionYear>
@@ -46,12 +47,12 @@
 		<imageTag>build-latest</imageTag>
 		<publishImage>false</publishImage>
 		<build.number>SET_BY_JENKINS</build.number>
-		<spring-cloud-config-server.version>4.1.2</spring-cloud-config-server.version>
+
+		<spring-cloud-config-server.version>4.2.0</spring-cloud-config-server.version>
 		<testcontainers-keycloak.version>3.3.1</testcontainers-keycloak.version>
-		<keycloak-admin-client.version>24.0.5</keycloak-admin-client.version>
+
 		<mongock.version>5.4.0</mongock.version>
-		<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
-		<mapstruct-processor.version>${mapstruct.version}</mapstruct-processor.version>
+
 		<zufi-manager.version>1.6.0</zufi-manager.version>
 		<shedlock.version>5.16.0</shedlock.version>
 	</properties>
@@ -66,42 +67,46 @@
 
 		<!-- Spring -->
 		<dependency>
-			<groupId>net.devh</groupId>
-			<artifactId>grpc-client-spring-boot-starter</artifactId>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
 		</dependency>
 		<dependency>
-			<groupId>io.grpc</groupId>
-			<artifactId>grpc-inprocess</artifactId>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-rest</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-actuator</artifactId>
+			<artifactId>spring-boot-starter-hateoas</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-data-mongodb</artifactId>
+			<artifactId>spring-boot-starter-security</artifactId>
 		</dependency>
+
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-web</artifactId>
+			<artifactId>spring-boot-starter-data-mongodb</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-data-rest</artifactId>
+			<artifactId>spring-boot-starter-actuator</artifactId>
 		</dependency>
+
 		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-hateoas</artifactId>
+			<groupId>net.devh</groupId>
+			<artifactId>grpc-client-spring-boot-starter</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>io.grpc</groupId>
+			<artifactId>grpc-inprocess</artifactId>
+		</dependency>
+
 		<dependency>
 			<groupId>org.springframework.cloud</groupId>
 			<artifactId>spring-cloud-config-server</artifactId>
 			<version>${spring-cloud-config-server.version}</version>
 		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-security</artifactId>
-		</dependency>
+
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-validation</artifactId>
@@ -132,7 +137,6 @@
 		<dependency>
 			<groupId>org.keycloak</groupId>
 			<artifactId>keycloak-admin-client</artifactId>
-			<version>${keycloak-admin-client.version}</version>
 		</dependency>
 
 		<!-- tools -->
@@ -143,7 +147,7 @@
 		<dependency>
 			<groupId>org.mapstruct</groupId>
 			<artifactId>mapstruct-processor</artifactId>
-			<version>${mapstruct-processor.version}</version>
+			<version>${mapstruct.version}</version>
 		</dependency>
 
 		<!-- commons -->
@@ -266,6 +270,7 @@
 				</plugin>
 			</plugins>
 		</pluginManagement>
+
 		<plugins>
 			<plugin>
 				<groupId>org.sonarsource.scanner.maven</groupId>
@@ -282,60 +287,10 @@
 			<plugin>
 				<groupId>org.springframework.boot</groupId>
 				<artifactId>spring-boot-maven-plugin</artifactId>
-				<configuration>
-					<image>
-						<name>${imageName}:${imageTag}</name>
-						<publish>${publishImage}</publish>
-					</image>
-					<docker>
-						<publishRegistry>
-							<username>${docker.publishRegistry.username}</username>
-							<password>${docker.publishRegistry.password}</password>
-						</publishRegistry>
-					</docker>
-					<excludes>
-						<exclude>
-							<groupId>org.projectlombok</groupId>
-							<artifactId>lombok</artifactId>
-						</exclude>
-					</excludes>
-				</configuration>
-				<executions>
-					<execution>
-						<id>build-info</id>
-						<goals>
-							<goal>build-info</goal>
-						</goals>
-						<configuration>
-							<additionalProperties>
-								<number>${build.number}</number>
-							</additionalProperties>
-						</configuration>
-					</execution>
-				</executions>
 			</plugin>
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-compiler-plugin</artifactId>
-				<configuration>
-					<annotationProcessorPaths>
-						<path>
-							<groupId>org.mapstruct</groupId>
-							<artifactId>mapstruct-processor</artifactId>
-							<version>${mapstruct-processor.version}</version>
-						</path>
-						<path>
-							<groupId>org.projectlombok</groupId>
-							<artifactId>lombok</artifactId>
-							<version>${lombok.version}</version>
-						</path>
-						<path>
-							<groupId>org.projectlombok</groupId>
-							<artifactId>lombok-mapstruct-binding</artifactId>
-							<version>${lombok-mapstruct-binding.version}</version>
-						</path>
-					</annotationProcessorPaths>
-				</configuration>
 			</plugin>
 		</plugins>
 	</build>
diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml
index 54775001fee442756defcbb005b29ebc6102c263..58ff6b8aeeff343f7af013e6c3ff8e22c31d326b 100644
--- a/src/main/helm/templates/deployment.yaml
+++ b/src/main/helm/templates/deployment.yaml
@@ -123,6 +123,10 @@ spec:
           - name: ozgcloud_feature_organisationsEinheiten
             value: {{ .Values.ozgcloud.feature.organisationsEinheiten | quote }}
           {{- end }}
+          {{- if eq ((.Values.ozgcloud).feature).organisationsEinheiten "true" }}
+          - name: ozgcloud_organisationeinheit_zufisearchuri
+            value: {{ required "ozgcloud.organisationEinheit.zufiSearchUri must be set if feature organisationsEinheiten is activated" ((.Values.ozgcloud).organisationEinheit).zufiSearchUri}}
+          {{- end }}
         envFrom:
           {{- if (.Values.database).useExternal }}
           - secretRef:
diff --git a/src/main/helm/values.yaml b/src/main/helm/values.yaml
index 6d9f58a3cdce518e86f0bca90594972ee87f2fd9..0fc8fa9bd51f4ff7d8cef1a9bc028fd0e948948b 100644
--- a/src/main/helm/values.yaml
+++ b/src/main/helm/values.yaml
@@ -34,7 +34,7 @@ ozgcloud:
       user: administrationApiUser
 
 zufiManager:
-  address: zufi-server.zufi:9090
+  address: dns:///zufi-server.zufi:9090
 
 image:
   repo: docker.ozg-sh.de
@@ -42,7 +42,7 @@ image:
   tag: latest # [default: latest]
 
 database:
-   databaseName: "administration-database"
-   secretName: "ozg-mongodb-admin-administration-user"
-   tls:
-      secretName: "ozg-mongodb-tls-cert"
+  databaseName: "administration-database"
+  secretName: "ozg-mongodb-admin-administration-user"
+  tls:
+    secretName: "ozg-mongodb-tls-cert"
diff --git a/src/main/java/de/ozgcloud/admin/AdministrationRepositoryRestConfigurer.java b/src/main/java/de/ozgcloud/admin/AdministrationRepositoryRestConfigurer.java
index 2ab9b687e22cad4e59a978b6558a6a366f60895a..31bcf3b864fd772838ebe1ef3555084c984af6b6 100644
--- a/src/main/java/de/ozgcloud/admin/AdministrationRepositoryRestConfigurer.java
+++ b/src/main/java/de/ozgcloud/admin/AdministrationRepositoryRestConfigurer.java
@@ -23,17 +23,20 @@
  */
 package de.ozgcloud.admin;
 
+import jakarta.validation.Validation;
+
 import org.springframework.context.annotation.Configuration;
-import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
+import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
 import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
-import org.springframework.hateoas.server.core.DefaultLinkRelationProvider;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
+
+import de.ozgcloud.admin.common.DelegatingValidatorAdapter;
 
 @Configuration
 public class AdministrationRepositoryRestConfigurer implements RepositoryRestConfigurer {
 
 	@Override
-	public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
-		config.setLinkRelationProvider(new DefaultLinkRelationProvider());
+	public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener listener) {
+		var validator = Validation.buildDefaultValidatorFactory().getValidator();
+		listener.addValidator("beforeCreate", new DelegatingValidatorAdapter(validator));
 	}
 }
diff --git a/src/main/java/de/ozgcloud/admin/OzgCloudRestLinksConfiguration.java b/src/main/java/de/ozgcloud/admin/OzgCloudRestLinksConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bf4c55db0af6b7785b80fac20e6c047a4f264c4
--- /dev/null
+++ b/src/main/java/de/ozgcloud/admin/OzgCloudRestLinksConfiguration.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2025 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.admin;
+
+import java.util.Optional;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.mapping.PersistentProperty;
+import org.springframework.data.mapping.context.PersistentEntities;
+import org.springframework.data.repository.support.Repositories;
+import org.springframework.data.rest.core.Path;
+import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
+import org.springframework.data.rest.core.mapping.PropertyAwareResourceMapping;
+import org.springframework.data.rest.core.mapping.RepositoryResourceMappings;
+import org.springframework.data.rest.core.mapping.ResourceDescription;
+import org.springframework.data.rest.core.mapping.ResourceMapping;
+import org.springframework.data.rest.core.mapping.ResourceMetadata;
+import org.springframework.data.rest.core.mapping.SearchResourceMappings;
+import org.springframework.data.rest.core.mapping.SupportedHttpMethods;
+import org.springframework.hateoas.LinkRelation;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.method.SecuredAuthorizationManager;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.util.SimpleMethodInvocation;
+
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Configuration
+class OzgCloudRestLinksConfiguration {
+	@Primary
+	@Bean
+	RepositoryResourceMappings ozgCloudResourceMappings(Repositories repositories, PersistentEntities persistentEntities,
+			RepositoryRestConfiguration repositoryRestConfiguration) {
+		return new OzgCloudDelegatingRepositoryResourceMappings(repositories, persistentEntities, repositoryRestConfiguration);
+	}
+}
+
+@Log4j2
+class OzgCloudDelegatingRepositoryResourceMappings extends RepositoryResourceMappings {
+
+	private final Repositories repositories;
+
+	public OzgCloudDelegatingRepositoryResourceMappings(Repositories repositories, PersistentEntities entities,
+			RepositoryRestConfiguration configuration) {
+		super(repositories, entities, configuration);
+
+		this.repositories = repositories;
+	}
+
+	@Override
+	public ResourceMetadata getMetadataFor(Class<?> type) {
+		return new OzgCloudDelegatingResourceMetadata(super.getMetadataFor(type), repositories);
+	}
+
+	@RequiredArgsConstructor
+	static class OzgCloudDelegatingResourceMetadata implements ResourceMetadata {
+
+		private final ResourceMetadata metadata;
+		private final Repositories repositories;
+		private final SecuredAuthorizationManager authManager = new SecuredAuthorizationManager();
+
+		@Override
+		public LinkRelation getItemResourceRel() {
+			return metadata.getItemResourceRel();
+		}
+
+		@Override
+		public ResourceDescription getItemResourceDescription() {
+			return metadata.getItemResourceDescription();
+		}
+
+		@Override
+		public Optional<Class<?>> getExcerptProjection() {
+			return metadata.getExcerptProjection();
+		}
+
+		@Override
+		public boolean isExported() {
+			return isAccessPermitted(metadata.getDomainType()) && metadata.isExported();
+		}
+
+		boolean isAccessPermitted(Class<?> type) {
+			var repository = repositories.getRepositoryFor(type);
+			return repository.map(repo -> authManager.check(() -> SecurityContextHolder.getContext().getAuthentication(), getFindAllInvocation(repo)))
+					.map(AuthorizationDecision::isGranted)
+					.orElse(true);
+		}
+
+		MethodInvocation getFindAllInvocation(Object repository) {
+			try {
+				var method = repository.getClass().getMethod("findAll");
+				return new SimpleMethodInvocation(repository, method);
+			} catch (NoSuchMethodException | SecurityException e) {
+				LOG.error(e.getMessage(), e);
+				throw new TechnicalException("Error on extracting findAll Repository Method", e);
+			}
+		}
+
+		@Override
+		public LinkRelation getRel() {
+			return metadata.getRel();
+		}
+
+		@Override
+		public Path getPath() {
+			return metadata.getPath();
+		}
+
+		@Override
+		public boolean isPagingResource() {
+			return metadata.isPagingResource();
+		}
+
+		@Override
+		public ResourceDescription getDescription() {
+			return metadata.getDescription();
+		}
+
+		@Override
+		public Class<?> getDomainType() {
+			return metadata.getDomainType();
+		}
+
+		@Override
+		public boolean isExported(PersistentProperty<?> property) {
+			return metadata.isExported(property);
+		}
+
+		@Override
+		public PropertyAwareResourceMapping getProperty(String mappedPath) {
+			return metadata.getProperty(mappedPath);
+		}
+
+		@Override
+		public ResourceMapping getMappingFor(PersistentProperty<?> property) {
+			return metadata.getMappingFor(property);
+		}
+
+		@Override
+		public SearchResourceMappings getSearchResourceMappings() {
+			return metadata.getSearchResourceMappings();
+		}
+
+		@Override
+		public SupportedHttpMethods getSupportedHttpMethods() {
+			return metadata.getSupportedHttpMethods();
+		}
+	}
+}
diff --git a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java
index 4d54c2e3ad0bbaee5d719022999b34a735022257..3b564cf37f4483a8b793e1f5c845fdfce4c9ad69 100644
--- a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java
+++ b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java
@@ -23,8 +23,9 @@
  */
 package de.ozgcloud.admin;
 
-import de.ozgcloud.admin.common.user.CurrentUserService;
-import de.ozgcloud.admin.common.user.UserRole;
+import java.util.ArrayList;
+import java.util.List;
+
 import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties;
 import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.Link;
@@ -32,33 +33,28 @@ import org.springframework.hateoas.server.RepresentationModelAssembler;
 import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
 import org.springframework.stereotype.Component;
 
+import de.ozgcloud.admin.common.user.CurrentUserService;
 import lombok.RequiredArgsConstructor;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @Component
 @RequiredArgsConstructor
 public class RootModelAssembler implements RepresentationModelAssembler<Root, EntityModel<Root>> {
 	static final String REL_CONFIGURATION = "configuration";
 
 	private final RepositoryRestProperties restProperties;
-
 	private final CurrentUserService currentUserService;
 
 	@Override
 	public EntityModel<Root> toModel(Root root) {
 		List<Link> links = buildRootModelLinks();
-		return EntityModel.of(
-				root,
-				links);
+		return EntityModel.of(root, links);
 	}
 
 	List<Link> buildRootModelLinks() {
 		List<Link> links = new ArrayList<>();
 		var rootLinkBuilder = WebMvcLinkBuilder.linkTo(RootController.class);
 		links.add(rootLinkBuilder.withSelfRel());
-		if (currentUserService.hasRole(UserRole.ADMIN_ADMIN)) {
+		if (currentUserService.hasConfigurationPermission()) {
 			links.add(buildConfigLink());
 		}
 		return links;
@@ -68,7 +64,6 @@ public class RootModelAssembler implements RepresentationModelAssembler<Root, En
 		var rootLinkBuilder = WebMvcLinkBuilder.linkTo(RootController.class);
 		return Link.of(
 				rootLinkBuilder.toUriComponentsBuilder().replacePath(restProperties.getBasePath()).toUriString(),
-				REL_CONFIGURATION
-		);
+				REL_CONFIGURATION);
 	}
 }
diff --git a/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceDeserializer.java b/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceDeserializer.java
deleted file mode 100644
index 106c3616b4a08d51e8ee413587fed710fc0dba98..0000000000000000000000000000000000000000
--- a/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceDeserializer.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.commons.lang3.StringUtils;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-
-import de.ozgcloud.common.datatype.StringBasedValue;
-import lombok.Getter;
-
-abstract class AbstractLinkedResourceDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
-
-	private static final long serialVersionUID = 1L;
-
-	@Getter
-	private BeanProperty beanProperty;
-
-	@Getter
-	private final JavaType targetType;
-
-	protected AbstractLinkedResourceDeserializer() {
-		super(Object.class);
-		targetType = null;
-	}
-
-	protected AbstractLinkedResourceDeserializer(BeanProperty beanProperty) {
-		super(Object.class);
-		this.beanProperty = beanProperty;
-		this.targetType = beanProperty.getType();
-	}
-
-	@Override
-	public Object deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
-		if (jsonParser.isExpectedStartArrayToken()) {
-			Collection<Object> idList = targetType.getRawClass().isAssignableFrom(Set.class) ? new HashSet<>() : new ArrayList<>();
-
-			while (!jsonParser.nextToken().isStructEnd()) {
-				idList.add(extractId(jsonParser.getText()));
-			}
-			return idList;
-		} else {
-			return extractId(jsonParser.getText());
-		}
-	}
-
-	Object extractId(String url) {
-		Class<?> type;
-		if (targetType.isCollectionLikeType()) {
-			type = targetType.getContentType().getRawClass();
-		} else {
-			type = targetType.getRawClass();
-		}
-
-		if (String.class.isAssignableFrom(type)) {
-			return extractStringId(url);
-		}
-		if (Long.class.isAssignableFrom(type) || Long.TYPE.isAssignableFrom(type)) {
-			return extractLongId(url);
-		}
-		if (StringBasedValue.class.isAssignableFrom(type)) {
-			return extractStringBasedValue(type, url);
-		}
-		return buildByBuilder(url);
-	}
-
-	abstract Object buildByBuilder(String url);
-
-	public static Long extractLongId(String uri) {
-		var trimedUri = StringUtils.trimToNull(uri);
-		if (Objects.isNull(trimedUri)) {
-			return null;
-		}
-		return Long.parseLong(URLDecoder.decode(trimedUri.substring(trimedUri.lastIndexOf('/') + 1), StandardCharsets.UTF_8));
-	}
-
-	private StringBasedValue extractStringBasedValue(Class<?> type, String url) {
-		String value = extractStringId(url);
-		Method fromMethod;
-		try {
-			fromMethod = type.getMethod("from", String.class);
-		} catch (NoSuchMethodException e) {
-			throw new IllegalStateException(
-					String.format("Cannot generate Id from type '%s'. Missing 'from' Method.", targetType.getRawClass().getSimpleName()));
-		}
-		try {
-			return (StringBasedValue) fromMethod.invoke(null, value);
-		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-			throw new IllegalStateException(
-					String.format("Cannot generate Id from type '%s'. Error calling 'from' Method.", targetType.getRawClass().getSimpleName()),
-					e);
-		}
-	}
-
-	public static String extractStringId(String url) {
-		return URLDecoder.decode(url.substring(url.lastIndexOf('/') + 1), StandardCharsets.UTF_8);
-	}
-}
diff --git a/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceSerializer.java b/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceSerializer.java
deleted file mode 100644
index aff16f22a29698cf591680353e4bf07800810d04..0000000000000000000000000000000000000000
--- a/src/main/java/de/ozgcloud/admin/common/AbstractLinkedResourceSerializer.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import java.io.IOException;
-import java.util.Collection;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.ContextualSerializer;
-
-import de.ozgcloud.common.errorhandling.TechnicalException;
-
-abstract class AbstractLinkedResourceSerializer extends JsonSerializer<Object> implements ContextualSerializer {
-	@Override
-	public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
-
-		if (value instanceof Collection) {
-			gen.writeStartArray();
-			((Collection<?>) value).forEach(val -> writeObject(gen, buildLink(val)));
-			gen.writeEndArray();
-		} else {
-			writeObject(gen, buildLink(value));
-		}
-	}
-
-	abstract String buildLink(Object id);
-
-	abstract IdExtractor<Object> getExtractor();
-
-	void writeObject(JsonGenerator gen, Object value) {
-		try {
-			gen.writeObject(value);
-		} catch (IOException e) {
-			throw new TechnicalException("Error writing String to json", e);
-		}
-	}
-}
diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingValidator.java b/src/main/java/de/ozgcloud/admin/common/DelegatingValidatorAdapter.java
similarity index 75%
rename from src/main/java/de/ozgcloud/admin/setting/SettingValidator.java
rename to src/main/java/de/ozgcloud/admin/common/DelegatingValidatorAdapter.java
index 54ecb425791b213835169e3303fc74b2296abe4c..870b17f79246c3db1491c81b56ad9fd8efab4c18 100644
--- a/src/main/java/de/ozgcloud/admin/setting/SettingValidator.java
+++ b/src/main/java/de/ozgcloud/admin/common/DelegatingValidatorAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -21,27 +21,30 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.setting;
+package de.ozgcloud.admin.common;
+
+import java.util.Objects;
 
 import jakarta.validation.ConstraintViolationException;
-import jakarta.validation.Validation;
 
-import org.springframework.stereotype.Component;
 import org.springframework.validation.Errors;
 import org.springframework.validation.Validator;
 
-@Component
-class SettingValidator implements Validator {
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class DelegatingValidatorAdapter implements Validator {
+
+	private final jakarta.validation.Validator delegate;
 
 	@Override
 	public boolean supports(Class<?> clazz) {
-		return Setting.class.equals(clazz);
+		return !Objects.isNull(delegate);
 	}
 
 	@Override
 	public void validate(Object target, Errors errors) {
-		var validator = Validation.buildDefaultValidatorFactory().getValidator();
-		var constraintViolations = validator.validate(target);
+		var constraintViolations = delegate.validate(target);
 		if (!constraintViolations.isEmpty()) {
 			throw new ConstraintViolationException(constraintViolations);
 		}
diff --git a/src/main/java/de/ozgcloud/admin/common/ObjectBuilder.java b/src/main/java/de/ozgcloud/admin/common/DtoService.java
similarity index 70%
rename from src/main/java/de/ozgcloud/admin/common/ObjectBuilder.java
rename to src/main/java/de/ozgcloud/admin/common/DtoService.java
index a08385af64361f8e178d9a03451f81853eeda9d8..6e260a3fb3ff15674ca5b88717426c40df1ec3ac 100644
--- a/src/main/java/de/ozgcloud/admin/common/ObjectBuilder.java
+++ b/src/main/java/de/ozgcloud/admin/common/DtoService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -23,11 +23,17 @@
  */
 package de.ozgcloud.admin.common;
 
-import com.fasterxml.jackson.databind.BeanProperty;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-public interface ObjectBuilder<T> {
+import org.springframework.stereotype.Service;
 
-	T build(Object id);
+@Service
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DtoService {
 
-	ObjectBuilder<T> constructContextAware(BeanProperty property);
+	String[] value();
 }
diff --git a/src/main/java/de/ozgcloud/admin/common/FeatureToggleProperties.java b/src/main/java/de/ozgcloud/admin/common/FeatureToggleProperties.java
index 75ab7ba16c52807514442a6a48be9e2d80c54086..71cdb9eec76431a104e4913e530401e73d415f6c 100644
--- a/src/main/java/de/ozgcloud/admin/common/FeatureToggleProperties.java
+++ b/src/main/java/de/ozgcloud/admin/common/FeatureToggleProperties.java
@@ -1,3 +1,26 @@
+/*
+ * 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.admin.common;
 
 import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -9,9 +32,11 @@ import lombok.Setter;
 @Setter
 @Getter
 @Configuration
-@ConfigurationProperties(prefix = "ozgcloud.feature")
+@ConfigurationProperties(prefix = FeatureToggleProperties.FEATURE_TOGGLE_PREFIX)
 public class FeatureToggleProperties {
 
+	public static final String FEATURE_TOGGLE_PREFIX = "ozgcloud.feature";
+
 	private boolean postfach;
 	private boolean benutzerRollen;
 	private boolean organisationsEinheiten;
diff --git a/src/main/java/de/ozgcloud/admin/common/LinkedResource.java b/src/main/java/de/ozgcloud/admin/common/LinkedResource.java
deleted file mode 100644
index 4798f516bf52910ab2641ecba167540622dfb101..0000000000000000000000000000000000000000
--- a/src/main/java/de/ozgcloud/admin/common/LinkedResource.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import org.springframework.core.annotation.AliasFor;
-
-import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
-@Inherited
-
-@JacksonAnnotationsInside
-@JsonSerialize(using = LinkedResourceSerializer.class)
-@JsonDeserialize(using = LinkedResourceDeserializer.class)
-public @interface LinkedResource {
-
-	Class<?> controllerClass();
-
-	Class<? extends IdExtractor<Object>> extractor() default ToStringExtractor.class;
-
-	@AliasFor(annotation = JsonDeserialize.class, attribute = "builder")
-	Class<? extends ObjectBuilder<Object>> builder() default IdBuilder.class;
-}
\ No newline at end of file
diff --git a/src/main/java/de/ozgcloud/admin/common/LinkedResourceDeserializer.java b/src/main/java/de/ozgcloud/admin/common/LinkedResourceDeserializer.java
deleted file mode 100644
index 7130e16af20c1f774e599b3096bced026c1b7bbe..0000000000000000000000000000000000000000
--- a/src/main/java/de/ozgcloud/admin/common/LinkedResourceDeserializer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import java.lang.reflect.InvocationTargetException;
-
-import org.apache.commons.lang3.reflect.ConstructorUtils;
-
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-
-import de.ozgcloud.common.errorhandling.TechnicalException;
-
-public class LinkedResourceDeserializer extends AbstractLinkedResourceDeserializer {
-
-	private static final long serialVersionUID = 1L;
-
-	private LinkedResource annotation;
-
-	protected LinkedResourceDeserializer() {
-		super();
-	}
-
-	protected LinkedResourceDeserializer(BeanProperty beanProperty) {
-		super(beanProperty);
-		this.annotation = beanProperty.getAnnotation(LinkedResource.class);
-	}
-
-	@Override
-	Object buildByBuilder(String url) {
-		ObjectBuilder<?> builder;
-		try {
-			builder = ConstructorUtils.invokeConstructor(annotation.builder()).constructContextAware(getBeanProperty());
-		} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
-			throw new TechnicalException("Error instanciating Builder.", e);
-		}
-		return builder.build(extractStringId(url));
-	}
-
-	@Override
-	public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
-		return new LinkedResourceDeserializer(property);
-	}
-
-}
diff --git a/src/main/java/de/ozgcloud/admin/common/LinkedResourceSerializer.java b/src/main/java/de/ozgcloud/admin/common/LinkedResourceSerializer.java
deleted file mode 100644
index 28a098c82185730476c0f85f4eb299a7bdd3ed13..0000000000000000000000000000000000000000
--- a/src/main/java/de/ozgcloud/admin/common/LinkedResourceSerializer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
-
-import java.lang.reflect.InvocationTargetException;
-
-import org.apache.commons.lang3.reflect.ConstructorUtils;
-
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-
-import de.ozgcloud.common.errorhandling.TechnicalException;
-import lombok.NoArgsConstructor;
-
-@NoArgsConstructor
-public class LinkedResourceSerializer extends AbstractLinkedResourceSerializer {
-
-	private LinkedResource annotation;
-
-	private LinkedResourceSerializer(LinkedResource annotation) {
-		this.annotation = annotation;
-	}
-
-	@Override
-	public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
-		return new LinkedResourceSerializer(property.getAnnotation(LinkedResource.class));
-	}
-
-	@Override
-	String buildLink(Object id) {
-		return linkTo(annotation.controllerClass()).slash(getExtractor().extractId(id)).toString();
-	}
-
-	@Override
-	IdExtractor<Object> getExtractor() {
-		try {
-			return ConstructorUtils.invokeConstructor(annotation.extractor());
-		} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
-			throw new TechnicalException("Error instanciating IdExtractor", e);
-		}
-	}
-}
diff --git a/src/main/java/de/ozgcloud/admin/common/ModelBuilder.java b/src/main/java/de/ozgcloud/admin/common/ModelBuilder.java
deleted file mode 100644
index cd90619981aa59db2f29a1f158c76000773aacb2..0000000000000000000000000000000000000000
--- a/src/main/java/de/ozgcloud/admin/common/ModelBuilder.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.BooleanSupplier;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.function.UnaryOperator;
-import java.util.stream.Collectors;
-
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.reflect.FieldUtils;
-import org.springframework.hateoas.EntityModel;
-import org.springframework.hateoas.Link;
-import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
-
-@Log4j2
-public class ModelBuilder<T> {
-
-	private static final Map<Class<?>, Map<Object, List<Field>>> ANNOTATED_FIELDS_BY_ANNOTATION = new ConcurrentHashMap<>();
-
-	private final T entity;
-	private final EntityModel<T> model;
-
-	private final List<Link> links = new LinkedList<>();
-	private final List<Function<EntityModel<T>, EntityModel<T>>> mapper = new LinkedList<>();
-
-	private ModelBuilder(T entity) {
-		this.entity = entity;
-		this.model = null;
-	}
-
-	private ModelBuilder(EntityModel<T> model) {
-		this.entity = null;
-		this.model = model;
-	}
-
-	public static <T> ModelBuilder<T> fromEntity(T entity) {
-		return new ModelBuilder<>(entity);
-	}
-
-	public static <T> ModelBuilder<T> fromModel(EntityModel<T> model) {
-		return new ModelBuilder<>(model);
-	}
-
-	public ModelBuilder<T> addLink(Link link) {
-		links.add(link);
-		return this;
-	}
-
-	public ModelBuilder<T> addLink(Optional<Link> link) {
-		link.ifPresent(links::add);
-		return this;
-	}
-
-	public ModelBuilder<T> addLinks(Link... links) {
-		this.links.addAll(Arrays.asList(links));
-		return this;
-	}
-
-	public ModelBuilder<T> addLinks(Collection<Link> links) {
-		this.links.addAll(links);
-		return this;
-	}
-
-	public ConditionalLinkAdder ifMatch(Predicate<T> predicate) {
-		return new ConditionalLinkAdder(predicate.test(Objects.isNull(entity) ? model.getContent() : entity));
-	}
-
-	public ConditionalLinkAdder ifMatch(BooleanSupplier guard) {
-		return new ConditionalLinkAdder(guard.getAsBoolean());
-	}
-
-	public ModelBuilder<T> map(UnaryOperator<EntityModel<T>> mapper) {
-		this.mapper.add(mapper);
-		return this;
-	}
-
-	public EntityModel<T> buildModel() {
-		var filteredLinks = links.stream().filter(Objects::nonNull).collect(Collectors.toSet());
-
-		EntityModel<T> buildedModel = Objects.isNull(model) ? EntityModel.of(entity) : model;
-		buildedModel = buildedModel.add(filteredLinks);
-
-		addLinkByLinkedResourceAnnotationIfMissing(buildedModel);
-
-		return applyMapper(buildedModel);
-	}
-
-	private EntityModel<T> applyMapper(EntityModel<T> resource) {
-		Iterator<Function<EntityModel<T>, EntityModel<T>>> i = mapper.iterator();
-		EntityModel<T> result = resource;
-		while (i.hasNext()) {
-			result = i.next().apply(result);
-		}
-		return result;
-	}
-
-	private void addLinkByLinkedResourceAnnotationIfMissing(EntityModel<T> resource) {
-		getFields(LinkedResource.class).stream()
-				.filter(field -> shouldAddLink(resource, field))
-				.forEach(field -> handleLinkedResourceField(resource, field));
-	}
-
-	private void handleLinkedResourceField(EntityModel<T> resource, Field field) {
-		getEntityFieldValue(field).map(Object::toString).filter(StringUtils::isNotBlank).ifPresent(val -> resource
-				.add(WebMvcLinkBuilder.linkTo(field.getAnnotation(LinkedResource.class).controllerClass()).slash(val)
-						.withRel(sanitizeName(field.getName()))));
-	}
-
-	private boolean shouldAddLink(EntityModel<T> resource, Field field) {
-		return !(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType()) || resource.hasLink(sanitizeName(field.getName())));
-	}
-
-	private List<Field> getFields(Class<? extends Annotation> annotationClass) {
-		var fields = Optional.ofNullable(ANNOTATED_FIELDS_BY_ANNOTATION.get(getEntity().getClass()))
-				.map(fieldsByAnnotation -> fieldsByAnnotation.get(annotationClass))
-				.orElseGet(Collections::emptyList);
-
-		if (CollectionUtils.isEmpty(fields)) {
-			fields = FieldUtils.getFieldsListWithAnnotation(getEntity().getClass(), annotationClass);
-
-			updateFields(annotationClass, fields);
-		}
-		return fields;
-	}
-
-	private void updateFields(Class<? extends Annotation> annotationClass, List<Field> fields) {
-		var annotationMap = Optional.ofNullable(ANNOTATED_FIELDS_BY_ANNOTATION.get(getEntity().getClass())).orElseGet(HashMap::new);
-		annotationMap.put(annotationClass, fields);
-
-		ANNOTATED_FIELDS_BY_ANNOTATION.put(annotationClass, annotationMap);
-	}
-
-	private String sanitizeName(String fieldName) {
-		if (fieldName.endsWith("Id")) {
-			return fieldName.substring(0, fieldName.indexOf("Id"));
-		}
-		return fieldName;
-	}
-
-	private Optional<Object> getEntityFieldValue(Field field) {
-		try {
-			field.setAccessible(true);
-			Optional<Object> value = Optional.ofNullable(field.get(getEntity()));
-			field.setAccessible(false);
-			return value;
-		} catch (IllegalArgumentException | IllegalAccessException e) {
-			LOG.warn("Cannot access field value of LinkedResource field.", e);
-		}
-		return Optional.empty();
-	}
-
-	private T getEntity() {
-		return Optional.ofNullable(entity == null ? model.getContent() : entity)
-				.orElseThrow(() -> new IllegalStateException("Entity must not null for ModelBuilding"));
-	}
-
-	@RequiredArgsConstructor
-	public class ConditionalLinkAdder {
-
-		public final boolean conditionFulfilled;
-
-		public ModelBuilder<T> addLink(Supplier<Link> linkSupplier) {
-			if (conditionFulfilled) {
-				addLink(linkSupplier.get());
-			}
-			return ModelBuilder.this;
-		}
-
-		public ModelBuilder<T> addLinkIfPresent(Supplier<Optional<Link>> linkSupplier) {
-			if (conditionFulfilled) {
-				addLink(linkSupplier.get());
-			}
-			return ModelBuilder.this;
-		}
-
-		public ModelBuilder<T> addLink(Function<T, Link> linkBuilder) {
-			if (conditionFulfilled) {
-				addLink(linkBuilder.apply(getEntity()));
-			}
-			return ModelBuilder.this;
-		}
-
-		public ModelBuilder<T> addLink(Link link) {
-			if (conditionFulfilled) {
-				links.add(link);
-			}
-			return ModelBuilder.this;
-		}
-
-		public ModelBuilder<T> addLink(Optional<Link> link) {
-			if (conditionFulfilled) {
-				link.ifPresent(links::add);
-			}
-			return ModelBuilder.this;
-		}
-
-		public ModelBuilder<T> addLinks(Link... links) {
-			if (conditionFulfilled) {
-				ModelBuilder.this.links.addAll(Arrays.asList(links));
-			}
-			return ModelBuilder.this;
-		}
-
-		@SafeVarargs
-		public final ModelBuilder<T> addLinks(Supplier<Link>... linkSuppliers) {
-			if (conditionFulfilled) {
-				for (int i = 0; i < linkSuppliers.length; i++) {
-					ModelBuilder.this.links.add(linkSuppliers[i].get());
-				}
-			}
-			return ModelBuilder.this;
-		}
-
-		public final ModelBuilder<T> addLinks(Supplier<Collection<Link>> linksSupplier) {
-			if (conditionFulfilled) {
-				linksSupplier.get().forEach(ModelBuilder.this.links::add);
-			}
-
-			return ModelBuilder.this;
-		}
-
-	}
-}
\ No newline at end of file
diff --git a/src/main/java/de/ozgcloud/admin/common/IdExtractor.java b/src/main/java/de/ozgcloud/admin/common/SettingDtoService.java
similarity index 84%
rename from src/main/java/de/ozgcloud/admin/common/IdExtractor.java
rename to src/main/java/de/ozgcloud/admin/common/SettingDtoService.java
index 658b06586cd447274ef701dc8942af43300a814a..e99addc6f1bc9cbf172a6c0be1baebbf29cd9812 100644
--- a/src/main/java/de/ozgcloud/admin/common/IdExtractor.java
+++ b/src/main/java/de/ozgcloud/admin/common/SettingDtoService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -23,7 +23,9 @@
  */
 package de.ozgcloud.admin.common;
 
-@FunctionalInterface
-public interface IdExtractor<T> {
-	String extractId(T object);
-}
\ No newline at end of file
+public interface SettingDtoService {
+
+	String getSettingKeyName();
+
+	Object getDataTransferObject();
+}
diff --git a/src/main/java/de/ozgcloud/admin/common/user/CurrentUserHelper.java b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserHelper.java
index b4fd2c459c18cd81dca191d69eb2da662c935c96..123ecc4d4751770e33f3a9234d20a22662932d93 100644
--- a/src/main/java/de/ozgcloud/admin/common/user/CurrentUserHelper.java
+++ b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserHelper.java
@@ -26,9 +26,10 @@ package de.ozgcloud.admin.common.user;
 import java.util.Collection;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.security.authentication.AuthenticationTrustResolver;
 import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
 import org.springframework.security.core.Authentication;
@@ -46,20 +47,25 @@ class CurrentUserHelper {
 	private static final Predicate<Authentication> IS_TRUSTED = auth -> !TRUST_RESOLVER.isAnonymous(auth);
 
 	public static boolean hasRole(String role) {
+		return hasAnyRole(Set.of(role));
+	}
+
+	public static boolean hasAnyRole(Set<String> roles) {
 		var auth = getAuthentication();
 
 		if ((Objects.isNull(auth)) || (Objects.isNull(auth.getPrincipal()))) {
 			return false;
 		}
 
-		return containsRole(auth.getAuthorities(), role);
+		return containsAnyRole(auth.getAuthorities(), roles);
 	}
 
-	static boolean containsRole(Collection<? extends GrantedAuthority> authorities, String role) {
+	static boolean containsAnyRole(Collection<? extends GrantedAuthority> authorities, Set<String> roles) {
 		if (Objects.isNull(authorities)) {
 			return false;
 		}
-		return authorities.stream().anyMatch(a -> StringUtils.equalsIgnoreCase(addRolePrefixIfMissing(role), a.getAuthority()));
+		var rolesWithPrefix = roles.stream().map(CurrentUserHelper::addRolePrefixIfMissing).collect(Collectors.toSet());
+		return authorities.stream().anyMatch(a -> rolesWithPrefix.contains(a.getAuthority()));
 	}
 
 	static String addRolePrefixIfMissing(String roleToCheck) {
diff --git a/src/main/java/de/ozgcloud/admin/common/user/CurrentUserService.java b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserService.java
index fc045ec0c3fcb19d9fc3b060bd140ceb0890368a..0959eafefcfcec06dccb4b75fe704928e21edc12 100644
--- a/src/main/java/de/ozgcloud/admin/common/user/CurrentUserService.java
+++ b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserService.java
@@ -30,4 +30,8 @@ public class CurrentUserService {
 	public boolean hasRole(String role) {
 		return CurrentUserHelper.hasRole(role);
 	}
+
+	public boolean hasConfigurationPermission() {
+		return CurrentUserHelper.hasAnyRole(UserRole.CONFIGURATION_ROLES);
+	}
 }
diff --git a/src/main/java/de/ozgcloud/admin/common/user/UserRole.java b/src/main/java/de/ozgcloud/admin/common/user/UserRole.java
index d4745c0a2a8bb515dcc694024383e187aee03f36..391e3f5a70f6b1865cef7b4e4e09d73f5d57870d 100644
--- a/src/main/java/de/ozgcloud/admin/common/user/UserRole.java
+++ b/src/main/java/de/ozgcloud/admin/common/user/UserRole.java
@@ -23,10 +23,15 @@
  */
 package de.ozgcloud.admin.common.user;
 
+import java.util.Set;
+
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class UserRole {
+	public static final String DATENBEAUFTRAGUNG = "DATENBEAUFTRAGUNG";
 	public static final String ADMIN_ADMIN = "ADMIN_ADMIN";
+
+	public static final Set<String> CONFIGURATION_ROLES = Set.of(ADMIN_ADMIN, DATENBEAUFTRAGUNG);
 }
diff --git a/src/main/java/de/ozgcloud/admin/environment/Features.java b/src/main/java/de/ozgcloud/admin/environment/Features.java
index 1f63a6e88be1cd2415b720916e60730bc3b3a41e..1e2ce9ab97d6011f858a4fe336031b25a3d54ee9 100644
--- a/src/main/java/de/ozgcloud/admin/environment/Features.java
+++ b/src/main/java/de/ozgcloud/admin/environment/Features.java
@@ -1,3 +1,26 @@
+/*
+ * 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.admin.environment;
 
 import lombok.Builder;
diff --git a/src/main/java/de/ozgcloud/admin/environment/FeaturesMapper.java b/src/main/java/de/ozgcloud/admin/environment/FeaturesMapper.java
index 2a7e9d4c3b0614824eb0d35fb4dd004e876327a0..d5ee1ad7f21580ce4c5c085e5a83314f6ae3b480 100644
--- a/src/main/java/de/ozgcloud/admin/environment/FeaturesMapper.java
+++ b/src/main/java/de/ozgcloud/admin/environment/FeaturesMapper.java
@@ -1,3 +1,26 @@
+/*
+ * 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.admin.environment;
 
 import org.mapstruct.Mapper;
diff --git a/src/main/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessor.java b/src/main/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1a750f6c95152b2eb1d94ea15d312f8e303f074
--- /dev/null
+++ b/src/main/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 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.admin.keycloak;
+
+import java.util.Map;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.Link;
+import org.springframework.hateoas.server.RepresentationModelProcessor;
+import org.springframework.stereotype.Component;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import de.ozgcloud.admin.Root;
+import de.ozgcloud.admin.common.FeatureToggleProperties;
+import de.ozgcloud.admin.common.user.CurrentUserService;
+import de.ozgcloud.admin.common.user.UserRole;
+import lombok.RequiredArgsConstructor;
+
+@Component
+@RequiredArgsConstructor
+@ConditionalOnProperty(prefix = FeatureToggleProperties.FEATURE_TOGGLE_PREFIX, name = "benutzer-rollen", havingValue = "true")
+class KeyCloakRootProcessor implements RepresentationModelProcessor<EntityModel<Root>> {
+
+	private static final String KEYCLOAK_API_TEMPLATE = "{baseUrl}/admin/realms/{realm}/users";
+	private static final String BASE_URL_KEY = "baseUrl";
+	private static final String REALM_KEY = "realm";
+
+	public static final String REL_USERS = "users";
+
+	private final KeycloakApiProperties keycloakApiProperties;
+	private final CurrentUserService currentUserService;
+
+	@Override
+	public EntityModel<Root> process(EntityModel<Root> model) {
+		return model.addIf(currentUserService.hasRole(UserRole.ADMIN_ADMIN), () -> Link.of(buildUsersHref(), REL_USERS));
+	}
+
+	String buildUsersHref() {
+		Map<String, Object> pathVariableMap = Map.of(BASE_URL_KEY, keycloakApiProperties.getUrl(), REALM_KEY, keycloakApiProperties.getRealm());
+		return UriComponentsBuilder.fromUriString(KEYCLOAK_API_TEMPLATE).uriVariables(pathVariableMap)
+				.build().toUriString();
+	}
+}
diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationEinheitProperties.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationEinheitProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..552ed8074437fbead4c365d191588319177d3d61
--- /dev/null
+++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationEinheitProperties.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 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.admin.organisationseinheit;
+
+import jakarta.validation.constraints.NotBlank;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import de.ozgcloud.admin.common.FeatureToggleProperties;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+@Configuration
+@ConfigurationProperties(prefix = OrganisationEinheitProperties.ORGANISATION_EINHEIT_PROPERTIES_PREFIX)
+@ConditionalOnProperty(prefix = FeatureToggleProperties.FEATURE_TOGGLE_PREFIX, name = "organisations-einheiten", havingValue = "true")
+class OrganisationEinheitProperties {
+
+	static final String ORGANISATION_EINHEIT_PROPERTIES_PREFIX = "ozgcloud.organisation-einheit";
+
+	/*
+	 * Uri pointing to the endpoint in Alfa that searches for Organisationseinheiten
+	 * in Zufi
+	 */
+	@NotBlank
+	private String zufiSearchUri;
+}
diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java
index 1cb98fd649ac08564bb8f9da24e5419c0e930016..8e613dda4bb29b3382e1ed2ecf10fa245b64f79a 100644
--- a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java
+++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java
@@ -25,25 +25,42 @@ package de.ozgcloud.admin.organisationseinheit;
 
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
 
+import java.util.List;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.Link;
 import org.springframework.hateoas.server.RepresentationModelProcessor;
 import org.springframework.stereotype.Component;
 
 import de.ozgcloud.admin.Root;
 import de.ozgcloud.admin.common.FeatureToggleProperties;
+import de.ozgcloud.admin.common.user.CurrentUserService;
+import de.ozgcloud.admin.common.user.UserRole;
 import lombok.RequiredArgsConstructor;
 
 @Component
 @RequiredArgsConstructor
+@ConditionalOnProperty(prefix = FeatureToggleProperties.FEATURE_TOGGLE_PREFIX, name = "organisations-einheiten", havingValue = "true")
 class OrganisationsEinheitRootProcessor implements RepresentationModelProcessor<EntityModel<Root>> {
 
-	static final String REL_ORGANISATIONS_EINHEITEN = "organisationsEinheiten";
+	static final String REL_ORGANISATION_EINHEITEN = "organisationsEinheiten";
+	static final String REL_SEARCH_ORGANISATION_EINHEIT = "searchOrganisationEinheit";
 
-	private final FeatureToggleProperties featureToggleProperties;
+	private final OrganisationEinheitProperties organisationsEinheitProperties;
+	private final CurrentUserService currentUserService;
 
 	@Override
 	public EntityModel<Root> process(EntityModel<Root> model) {
-		return model.addIf(featureToggleProperties.isOrganisationsEinheiten(),
-				() -> linkTo(methodOn(OrganisationsEinheitController.class).getAll()).withRel(REL_ORGANISATIONS_EINHEITEN));
+		return model.addAllIf(currentUserService.hasRole(UserRole.ADMIN_ADMIN),
+				() -> List.of(buildGetAllOrganisationEinheitenLink(), buildSearchOrganisationEinheitLink()));
+	}
+
+	private Link buildGetAllOrganisationEinheitenLink() {
+		return linkTo(methodOn(OrganisationsEinheitController.class).getAll()).withRel(REL_ORGANISATION_EINHEITEN);
+	}
+
+	private Link buildSearchOrganisationEinheitLink() {
+		return Link.of(organisationsEinheitProperties.getZufiSearchUri(), REL_SEARCH_ORGANISATION_EINHEIT);
 	}
 }
diff --git a/src/main/java/de/ozgcloud/admin/reporting/AggregationMapping.java b/src/main/java/de/ozgcloud/admin/reporting/AggregationMapping.java
new file mode 100644
index 0000000000000000000000000000000000000000..d923d2c01a69af97787af47de8444cab6d086dd4
--- /dev/null
+++ b/src/main/java/de/ozgcloud/admin/reporting/AggregationMapping.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2025 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.admin.reporting;
+
+import java.util.List;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.TypeAlias;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+import lombok.ToString;
+import lombok.extern.jackson.Jacksonized;
+
+@Document("settings")
+@TypeAlias(AggregationMapping.TYPE_ALIAS)
+@Jacksonized
+@Builder
+@Getter
+@ToString
+public class AggregationMapping {
+
+	public static final String TYPE_ALIAS = "AggregationMapping";
+
+	@Id
+	@JsonIgnore
+	private String id;
+	@Builder.Default
+	private String name = "aggregationMapping";
+
+	@Valid
+	private FormIdentifier formIdentifier;
+
+	@NotEmpty
+	@Singular
+	private List<FieldMapping> mappings;
+
+	@Builder
+	@Getter
+	@ToString
+	static class FieldMapping {
+		@NotBlank
+		private String sourcePath;
+		@NotBlank
+		private String targetPath;
+	}
+
+	@Builder
+	@Getter
+	@ToString
+	static class FormIdentifier {
+		@NotBlank
+		private String formEngineName;
+		@NotBlank
+		private String formId;
+	}
+}
diff --git a/src/test/java/de/ozgcloud/admin/common/EntityModelTestFactory.java b/src/main/java/de/ozgcloud/admin/reporting/AggregationMappingMapper.java
similarity index 61%
rename from src/test/java/de/ozgcloud/admin/common/EntityModelTestFactory.java
rename to src/main/java/de/ozgcloud/admin/reporting/AggregationMappingMapper.java
index 1f01a94247938090c4e85f0d9c96d3ec399aeb6c..81a25c3042c172767fa810162764de2d72b9b3d9 100644
--- a/src/test/java/de/ozgcloud/admin/common/EntityModelTestFactory.java
+++ b/src/main/java/de/ozgcloud/admin/reporting/AggregationMappingMapper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -21,23 +21,18 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.common;
+package de.ozgcloud.admin.reporting;
 
-import org.springframework.hateoas.EntityModel;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
 
-import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheit;
-import lombok.NoArgsConstructor;
+import de.ozgcloud.admin.reporting.ReportingAggregationManagerSettingDto.AggregationMappingDto;
 
-public class EntityModelTestFactory {
+@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
+public interface AggregationMappingMapper {
 
-	public static final NullableEntityModel NULLABLE = createNullable();
-
-	private static NullableEntityModel createNullable() {
-		return new NullableEntityModel();
-	}
-
-	@NoArgsConstructor
-	private static class NullableEntityModel extends EntityModel<OrganisationsEinheit> {
-
-	}
+	@Mapping(target = "fieldMapping", ignore = true)
+	@Mapping(target = "fieldMappings", source = "mappings")
+	AggregationMappingDto toMapping(AggregationMapping setting);
 }
diff --git a/src/main/java/de/ozgcloud/admin/common/ToStringExtractor.java b/src/main/java/de/ozgcloud/admin/reporting/AggregationMappingRepository.java
similarity index 59%
rename from src/main/java/de/ozgcloud/admin/common/ToStringExtractor.java
rename to src/main/java/de/ozgcloud/admin/reporting/AggregationMappingRepository.java
index dade3573176baac48e7f6df83e0c45a4c1cb9389..249aa175f5c7a313ba1e69dd178926fd29c8f2a0 100644
--- a/src/main/java/de/ozgcloud/admin/common/ToStringExtractor.java
+++ b/src/main/java/de/ozgcloud/admin/reporting/AggregationMappingRepository.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -21,15 +21,19 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.common;
+package de.ozgcloud.admin.reporting;
 
-import lombok.NoArgsConstructor;
+import java.util.List;
 
-@NoArgsConstructor
-class ToStringExtractor implements IdExtractor<Object> {
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.data.mongodb.repository.Query;
+import org.springframework.data.rest.core.annotation.RepositoryRestResource;
+import org.springframework.security.access.annotation.Secured;
 
+@Secured("ROLE_DATENBEAUFTRAGUNG")
+@RepositoryRestResource
+interface AggregationMappingRepository extends MongoRepository<AggregationMapping, String> {
 	@Override
-	public String extractId(Object object) {
-		return object.toString();
-	}
+	@Query("{'_class': 'AggregationMapping'}")
+	List<AggregationMapping> findAll();
 }
diff --git a/src/main/java/de/ozgcloud/admin/setting/DataRestConfiguration.java b/src/main/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDto.java
similarity index 54%
rename from src/main/java/de/ozgcloud/admin/setting/DataRestConfiguration.java
rename to src/main/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDto.java
index b93f96d8665b904d133d44f9e3682b649e5ed5a6..00e74b76d4eaad2b3bbe9937c781f505852ff942 100644
--- a/src/main/java/de/ozgcloud/admin/setting/DataRestConfiguration.java
+++ b/src/main/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDto.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -21,23 +21,42 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.setting;
+package de.ozgcloud.admin.reporting;
 
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
-import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
+import java.util.List;
 
-import lombok.RequiredArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+import lombok.ToString;
 
-@Configuration
-@RequiredArgsConstructor
-public class DataRestConfiguration implements RepositoryRestConfigurer {
+@Builder
+@Getter
+@ToString
+public class ReportingAggregationManagerSettingDto {
 
-	private final SettingValidator settingValidator;
+	@Singular
+	private List<AggregationMappingDto> aggregationMappings;
 
-	@Override
-	public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
-		v.addValidator("beforeSave", settingValidator);
-		v.addValidator("beforeCreate", settingValidator);
+	@Builder
+	@Getter
+	static class AggregationMappingDto {
+		private FormIdentifierDto formIdentifier;
+		@Singular
+		private List<FieldMappingDto> fieldMappings;
 	}
-}
\ No newline at end of file
+
+	@Builder
+	@Getter
+	static class FormIdentifierDto {
+		private String formEngineName;
+		private String formId;
+	}
+
+	@Builder
+	@Getter
+	static class FieldMappingDto {
+		private String sourcePath;
+		private String targetPath;
+	}
+}
diff --git a/src/main/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDtoService.java b/src/main/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDtoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..acb46bf40ebaf81a11b896ed6c2d55c7048f29e7
--- /dev/null
+++ b/src/main/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDtoService.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2025 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.admin.reporting;
+
+import java.util.stream.Stream;
+
+import de.ozgcloud.admin.common.DtoService;
+import de.ozgcloud.admin.common.SettingDtoService;
+import de.ozgcloud.admin.setting.SettingConstants;
+import de.ozgcloud.admin.setting.SettingRepository;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@DtoService(SettingConstants.AGGREGATION_MANAGER)
+class ReportingAggregationManagerSettingDtoService implements SettingDtoService {
+
+	private final AggregationMappingMapper mapper;
+
+	private final SettingRepository repository;
+
+	@Override
+	public ReportingAggregationManagerSettingDto getDataTransferObject() {
+		return mapToDto(repository.findByType("AggregationMapping", AggregationMapping.class));
+	}
+
+	ReportingAggregationManagerSettingDto mapToDto(Stream<AggregationMapping> settings) {
+		return ReportingAggregationManagerSettingDto.builder()
+				.aggregationMappings(settings.map(mapper::toMapping).toList())
+				.build();
+	}
+
+	@Override
+	public String getSettingKeyName() {
+		return "aggregation";
+	}
+}
diff --git a/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java b/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java
index b3d46b9d610865098fc760e8a94e94ee79f594f2..9d8cd455bdd726f2d31207ac96c8740cb4139dbd 100644
--- a/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java
+++ b/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java
@@ -56,16 +56,13 @@ import lombok.RequiredArgsConstructor;
 @RequiredArgsConstructor
 public class SecurityConfiguration {
 
-	private final AdminAuthenticationEntryPoint authenticationEntryPoint;
-
-	private final OAuth2Properties oAuth2Properties;
-
 	static final String RESOURCE_ACCESS_KEY = "resource_access";
-
 	static final String SIMPLE_GRANT_AUTHORITY_PREFIX = "ROLE_";
-
 	static final String ROLES_KEY = "roles";
 
+	private final AdminAuthenticationEntryPoint authenticationEntryPoint;
+	private final OAuth2Properties oAuth2Properties;
+
 	@Bean
 	SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
 
@@ -77,8 +74,8 @@ public class SecurityConfiguration {
 
 		http.authorizeHttpRequests(requests -> requests
 				.requestMatchers(HttpMethod.GET, "/api/environment").permitAll()
-				.requestMatchers("/api/configuration").hasRole(UserRole.ADMIN_ADMIN)
-				.requestMatchers("/api/configuration/**").hasRole(UserRole.ADMIN_ADMIN)
+				.requestMatchers("/api/configuration").hasAnyRole(UserRole.ADMIN_ADMIN, UserRole.DATENBEAUFTRAGUNG)
+				.requestMatchers("/api/configuration/**").hasAnyRole(UserRole.ADMIN_ADMIN, UserRole.DATENBEAUFTRAGUNG)
 				.requestMatchers("/api").authenticated()
 				.requestMatchers("/api/**").authenticated()
 				.requestMatchers("/actuator").permitAll()
@@ -89,11 +86,11 @@ public class SecurityConfiguration {
 		return http.build();
 	}
 
+	// TODO OZG-4954 replace with spring defaults
 	@Bean
 	JwtAuthenticationConverter jwtAuthenticationConverter() {
 		var jwtConverter = new JwtAuthenticationConverter();
-		jwtConverter.setJwtGrantedAuthoritiesConverter(
-				this::convertJwtToGrantedAuthorities);
+		jwtConverter.setJwtGrantedAuthoritiesConverter(this::convertJwtToGrantedAuthorities);
 		jwtConverter.setPrincipalClaimName(StandardClaimNames.PREFERRED_USERNAME);
 		return jwtConverter;
 	}
diff --git a/src/test/java/de/ozgcloud/admin/common/LinkedResourceTestObject.java b/src/main/java/de/ozgcloud/admin/setting/MapBasedApplicationSettingDto.java
similarity index 67%
rename from src/test/java/de/ozgcloud/admin/common/LinkedResourceTestObject.java
rename to src/main/java/de/ozgcloud/admin/setting/MapBasedApplicationSettingDto.java
index d30ab26ef5568705572702d1e5401c215a5b40cb..178302ff18d14392d8f33ab8249c40c32c13c7b9 100644
--- a/src/test/java/de/ozgcloud/admin/common/LinkedResourceTestObject.java
+++ b/src/main/java/de/ozgcloud/admin/setting/MapBasedApplicationSettingDto.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -21,18 +21,25 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.common;
+package de.ozgcloud.admin.setting;
 
-import de.ozgcloud.admin.common.user.TestId;
-import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitController;
-import lombok.AllArgsConstructor;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+
+import lombok.Builder;
 import lombok.Getter;
-import lombok.NoArgsConstructor;
+import lombok.Singular;
 
+@Builder
 @Getter
-@AllArgsConstructor
-@NoArgsConstructor
-class LinkedResourceTestObject {
-	@LinkedResource(controllerClass = OrganisationsEinheitController.class)
-	private TestId id;
+public class MapBasedApplicationSettingDto implements ApplicationSettingDTO {
+
+	@Singular
+	private Map<String, Object> settings;
+
+	@JsonAnyGetter
+	public Map<String, Object> getSettings() {
+		return settings;
+	}
 }
diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingConstants.java b/src/main/java/de/ozgcloud/admin/setting/SettingConstants.java
index 9c40c6120a702447e27ebc8093025568763de5d3..afcf09344d8f76dc3519c635b99bfc933e185d24 100644
--- a/src/main/java/de/ozgcloud/admin/setting/SettingConstants.java
+++ b/src/main/java/de/ozgcloud/admin/setting/SettingConstants.java
@@ -32,4 +32,6 @@ public class SettingConstants {
 	static final String REL = "settings";
 
 	public static final String PATH = "settings";
+
+	public static final String AGGREGATION_MANAGER = "OzgCloud_AggregationManager";
 }
diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingEnvironmentRepository.java b/src/main/java/de/ozgcloud/admin/setting/SettingEnvironmentRepository.java
index db793f2f49805d18a48037b96817cdd1aa4c88e3..a6e74eac7d909eeefc363eebb43cb5cf078e49e5 100644
--- a/src/main/java/de/ozgcloud/admin/setting/SettingEnvironmentRepository.java
+++ b/src/main/java/de/ozgcloud/admin/setting/SettingEnvironmentRepository.java
@@ -23,6 +23,8 @@
  */
 package de.ozgcloud.admin.setting;
 
+import static de.ozgcloud.admin.setting.SettingConstants.*;
+
 import java.util.Map;
 import java.util.Optional;
 
@@ -41,13 +43,12 @@ import lombok.RequiredArgsConstructor;
 @Component
 public class SettingEnvironmentRepository implements EnvironmentRepository {
 
-	private final SettingService settingService;
-
-	private final ObjectMapper objectMapper;
-
 	private static final String ALFA = "Alfa";
 	private static final String VORGANG_MANAGER = "OzgCloud_VorgangManager";
 
+	private final SettingService settingService;
+	private final ObjectMapper objectMapper;
+
 	@Override
 	public Environment findOne(String application, String profile, String label) {
 		return buildEnvironment(application, findAnwendungSettingDTO(application));
@@ -59,6 +60,8 @@ public class SettingEnvironmentRepository implements EnvironmentRepository {
 				return Optional.of(settingService.getAlfaSettingDTO());
 			case VORGANG_MANAGER:
 				return Optional.of(settingService.getVorgangManagerSettingDTO());
+			case AGGREGATION_MANAGER:
+				return Optional.of(settingService.getAggregationManagerSettingDto());
 			default:
 				return Optional.empty();
 		}
diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingRepository.java b/src/main/java/de/ozgcloud/admin/setting/SettingRepository.java
index 5180dea45a85d6007717a4e2a3d64cbb6ad7c6d9..ddeeae9fd0a5cb80dec50bf3c9b5c41492318c3a 100644
--- a/src/main/java/de/ozgcloud/admin/setting/SettingRepository.java
+++ b/src/main/java/de/ozgcloud/admin/setting/SettingRepository.java
@@ -23,13 +23,25 @@
  */
 package de.ozgcloud.admin.setting;
 
+import java.util.List;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.data.mongodb.repository.Query;
 import org.springframework.data.rest.core.annotation.RepositoryRestResource;
+import org.springframework.security.access.annotation.Secured;
 
 @RepositoryRestResource(collectionResourceRel = SettingConstants.REL, path = SettingConstants.PATH)
-interface SettingRepository extends MongoRepository<Setting, String> {
+public interface SettingRepository extends MongoRepository<Setting, String> {
+
+	@Secured("ROLE_ADMIN_ADMIN")
+	@Override
+	@Query("{'_class': 'Setting'}")
+	List<Setting> findAll();
 
 	Optional<Setting> findOneByName(String name);
+
+	@Query("{'_class': ?0}")
+	<T> Stream<T> findByType(String typeName, Class<T> clazz);
 }
diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingService.java b/src/main/java/de/ozgcloud/admin/setting/SettingService.java
index 51b8529add8a7e42cb367f1fc6b4a062f8eaa4cb..aa9c8e2d7eea1a4eadf373331b4c369578968218 100644
--- a/src/main/java/de/ozgcloud/admin/setting/SettingService.java
+++ b/src/main/java/de/ozgcloud/admin/setting/SettingService.java
@@ -23,11 +23,25 @@
  */
 package de.ozgcloud.admin.setting;
 
+import static de.ozgcloud.admin.setting.SettingConstants.*;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import jakarta.annotation.PostConstruct;
 
+import org.apache.commons.lang3.tuple.Pair;
+import org.springframework.beans.factory.ListableBeanFactory;
 import org.springframework.stereotype.Service;
 
+import static java.util.stream.Collectors.*;
+
+import de.ozgcloud.admin.common.DtoService;
+import de.ozgcloud.admin.common.SettingDtoService;
 import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitSettings;
 import de.ozgcloud.admin.setting.postfach.PostfachSettingBody;
 import lombok.RequiredArgsConstructor;
@@ -41,6 +55,22 @@ class SettingService {
 	private final SettingRepository repository;
 
 	private final SettingMapper mapper;
+	private final ListableBeanFactory beanFactory;
+
+	private Map<String, List<SettingDtoService>> dtoServiceByManager;
+
+	@PostConstruct
+	void initDtoServices() {
+		this.dtoServiceByManager = beanFactory.getBeansWithAnnotation(DtoService.class).values().stream()
+				.map(SettingDtoService.class::cast)
+				.flatMap(this::getManagerForService)
+				.collect(Collectors.groupingBy(Pair::getKey, mapping(Pair::getValue, toList())));
+	}
+
+	private Stream<Pair<String, SettingDtoService>> getManagerForService(SettingDtoService dtoService) {
+		var annotation = dtoService.getClass().getDeclaredAnnotation(DtoService.class);
+		return Arrays.stream(annotation.value()).map(manager -> Pair.of(manager, dtoService));
+	}
 
 	public AlfaSettingDTO getAlfaSettingDTO() {
 		var postfachData = getSettingWithPostfachFromDb();
@@ -75,4 +105,20 @@ class SettingService {
 				OrganisationsEinheitWithSettings::organisationsEinheitId,
 				OrganisationsEinheitWithSettings::settings));
 	}
+
+	public ApplicationSettingDTO getAggregationManagerSettingDto() {
+		return MapBasedApplicationSettingDto.builder()
+				.settings(buildSettingsMap(AGGREGATION_MANAGER))
+				.build();
+	}
+
+	private Map<String, Object> buildSettingsMap(String managerName) {
+		return getDtoService(managerName).stream()
+				.collect(Collectors.toMap(SettingDtoService::getSettingKeyName, SettingDtoService::getDataTransferObject));
+	}
+
+	private List<SettingDtoService> getDtoService(String managerName) {
+		return dtoServiceByManager.getOrDefault(managerName, Collections.emptyList());
+	}
+
 }
diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml
index c5bb57b37de68a6c4b610d764df4023e48e248f4..7faeb3acc343d6320c0c77fcfcd3da7aca6dffbb 100644
--- a/src/main/resources/application-local.yaml
+++ b/src/main/resources/application-local.yaml
@@ -1,3 +1,6 @@
+logging:
+  config: classpath:log4j2-local.xml
+
 spring:
   data:
     mongodb:
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index f4a4954da71dc34a7be41330ce9d37169793c969..0ff2d17a8b35216926b5e4f4732c8d30fba13146 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -21,9 +21,27 @@
 # unter der Lizenz sind dem Lizenztext zu entnehmen.
 logging:
   level:
-    ROOT: INFO
+    ROOT: WARN
     '[de.ozgcloud]': INFO
 
+spring:
+  application:
+    name: OzgCloud_Administration
+  data:
+    mongodb:
+      authentication-database: admin
+    rest:
+      basePath: /api/configuration
+  cloud:
+    config:
+      server:
+        prefix: /configserver
+  security:
+    oauth2:
+      resourceserver:
+        jwt:
+          issuer-uri: ${ozgcloud.oauth2.auth-server-url}/realms/${ozgcloud.oauth2.realm}
+
 management:
   server:
     port: 8081
@@ -55,23 +73,7 @@ mongock:
     - de.ozgcloud.admin.migration
   enabled: true
 
-spring:
-  application:
-    name: OzgCloud_Administration
-  data:
-    mongodb:
-      authentication-database: admin
-    rest:
-      basePath: /api/configuration
-  cloud:
-    config:
-      server:
-        prefix: /configserver
-  security:
-    oauth2:
-      resourceserver:
-        jwt:
-          issuer-uri: ${ozgcloud.oauth2.auth-server-url}/realms/${ozgcloud.oauth2.realm}
+
 
 ozgcloud:
   keycloak:
diff --git a/src/test/helm/deployment_features_test.yaml b/src/test/helm/deployment_features_test.yaml
index 9e02c58d077d05b1f50bc15e8a701a36e6c4ac6f..d1d562160ef83483e3d37c7f939865b7baac18e2 100644
--- a/src/test/helm/deployment_features_test.yaml
+++ b/src/test/helm/deployment_features_test.yaml
@@ -60,6 +60,8 @@ tests:
           postfach: "true"
           benutzerRollen: "true"
           organisationsEinheiten: "true"
+        organisationEinheit:
+          zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy}
     asserts:
       - contains:
           path: spec.template.spec.containers[0].env
@@ -69,7 +71,7 @@ tests:
       - contains:
           path: spec.template.spec.containers[0].env
           content:
-            name:  ozgcloud_feature_benutzerRollen
+            name: ozgcloud_feature_benutzerRollen
             value: "true"
       - contains:
           path: spec.template.spec.containers[0].env
@@ -98,4 +100,4 @@ tests:
           path: spec.template.spec.containers[0].env
           content:
             name: ozgcloud_feature_organisationsEinheiten
-            value: "false"
\ No newline at end of file
+            value: "false"
diff --git a/src/test/helm/deployment_zufi_test.yaml b/src/test/helm/deployment_zufi_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e342ff4be91b397f84da64cf8606fc8d99f6e082
--- /dev/null
+++ b/src/test/helm/deployment_zufi_test.yaml
@@ -0,0 +1,83 @@
+#
+# Copyright (C) 2025 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 zufi search property
+release:
+  name: administration
+  namespace: sh-helm-test
+templates:
+  - templates/deployment.yaml
+set:
+  ozgcloud:
+    bundesland: sh
+    bezeichner: helm
+  sso:
+    serverUrl: https://sso.company.local
+  imagePullSecret: image-pull-secret
+tests:
+  - it: should not contain zufi search uri if organisationseinheit feature toggle not set
+    set:
+      ozgcloud:
+        feature:
+          organisationsEinheiten: "false"
+        organisationEinheit:
+          zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy}
+    asserts:
+      - notContains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_organisationeinheit_zufisearchuri
+          any: true
+  - it: should not contain zufi search uri if organisationseinheit feature toggle is disabled
+    set:
+      ozgcloud:
+        organisationEinheit:
+          zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy}
+    asserts:
+      - notContains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_organisationeinheit_zufisearchuri
+          any: true
+  - it: should set zufi search uri
+    set:
+      ozgcloud:
+        feature:
+          organisationsEinheiten: "true"
+        organisationEinheit:
+          zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy}
+    asserts:
+      - contains:
+          path: spec.template.spec.containers[0].env
+          content:
+            name: ozgcloud_organisationeinheit_zufisearchuri
+            value: alfa/zufi/search/endpoint?searchBy={searchBy}
+  - it: should fail if zufi search uri is not set
+    set:
+      ozgcloud:
+        feature:
+          organisationsEinheiten: "true"
+    asserts:
+      - failedTemplate:
+          errorMessage: "ozgcloud.organisationEinheit.zufiSearchUri must be set if feature organisationsEinheiten is activated"
diff --git a/src/test/java/de/ozgcloud/admin/AdministrationRepositoryRestConfigurerTest.java b/src/test/java/de/ozgcloud/admin/AdministrationRepositoryRestConfigurerTest.java
deleted file mode 100644
index a45fc711558b6073f96283db4ce04e98ad691077..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/admin/AdministrationRepositoryRestConfigurerTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InjectMocks;
-import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
-import org.springframework.hateoas.server.LinkRelationProvider;
-import org.springframework.hateoas.server.core.DefaultLinkRelationProvider;
-
-class AdministrationRepositoryRestConfigurerTest {
-
-	@InjectMocks
-	private AdministrationRepositoryRestConfigurer configurer;
-
-	@Nested
-	class TestConfigureRepositoryRestConfiguration {
-
-		@Captor
-		private ArgumentCaptor<LinkRelationProvider> linkRelationProviderArgumentCaptor;
-
-		@Test
-		void shouldUseDefaultLinkRelationProvider() {
-			var configuration = mock(RepositoryRestConfiguration.class);
-
-			configurer.configureRepositoryRestConfiguration(configuration, null);
-
-			verify(configuration).setLinkRelationProvider(linkRelationProviderArgumentCaptor.capture());
-			assertThat(linkRelationProviderArgumentCaptor.getValue()).isInstanceOf(DefaultLinkRelationProvider.class);
-		}
-	}
-
-}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/OzgCloudDelegatingResourceMetadataTest.java b/src/test/java/de/ozgcloud/admin/OzgCloudDelegatingResourceMetadataTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..12253c9025542a549059c4979f7cbc458c151c36
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/OzgCloudDelegatingResourceMetadataTest.java
@@ -0,0 +1,152 @@
+package de.ozgcloud.admin;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Optional;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.data.repository.support.Repositories;
+import org.springframework.data.rest.core.mapping.ResourceMetadata;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.method.SecuredAuthorizationManager;
+
+import de.ozgcloud.admin.OzgCloudDelegatingRepositoryResourceMappings.OzgCloudDelegatingResourceMetadata;
+import de.ozgcloud.common.test.ReflectionTestUtils;
+
+class OzgCloudDelegatingResourceMetadataTest {
+
+	@Spy
+	@InjectMocks
+	private OzgCloudDelegatingResourceMetadata delegatingResourceMetadata;
+
+	@Mock
+	private ResourceMetadata metadata;
+	@Mock
+	private Repositories repositories;
+	@Mock
+	private SecuredAuthorizationManager authManager;
+
+	@Nested
+	class TestIsExported {
+
+		@Test
+		void shouldReturnTrue() {
+			when(metadata.isExported()).thenReturn(true);
+			doReturn(true).when(delegatingResourceMetadata).isAccessPermitted(any());
+
+			var result = delegatingResourceMetadata.isExported();
+
+			assertThat(result).isTrue();
+		}
+
+		@Test
+		void shouldReturnFalseIfNotPermitted() {
+			doReturn(false).when(delegatingResourceMetadata).isAccessPermitted(any());
+
+			var result = delegatingResourceMetadata.isExported();
+
+			assertThat(result).isFalse();
+		}
+
+		@Test
+		void shouldReturnFalseIfNotExported() {
+			doReturn(true).when(delegatingResourceMetadata).isAccessPermitted(any());
+			when(metadata.isExported()).thenReturn(false);
+
+			var result = delegatingResourceMetadata.isExported();
+
+			assertThat(result).isFalse();
+		}
+	}
+
+	@Nested
+	class TestIsAccessPermitted {
+		@Mock
+		private Object repository;
+		@Mock
+		private AuthorizationDecision decision;
+
+		private final MethodInvocation invocation = Mockito.mock(MethodInvocation.class);
+
+		@BeforeEach
+		void initMock() {
+			when(repositories.getRepositoryFor(any())).thenReturn(Optional.of(repository));
+
+			doReturn(invocation).when(delegatingResourceMetadata).getFindAllInvocation(any());
+		}
+
+		@BeforeEach
+		void mockAuthManager() {
+			when(authManager.check(any(), any())).thenReturn(decision);
+
+			ReflectionTestUtils.setField(delegatingResourceMetadata, "authManager", authManager);
+		}
+
+		@Test
+		void shouldCallDecisionManager() {
+			delegatingResourceMetadata.isAccessPermitted(Object.class);
+
+			verify(decision).isGranted();
+		}
+
+		@Test
+		void shouldReturnTrueIfGranted() {
+			when(decision.isGranted()).thenReturn(true);
+
+			var result = delegatingResourceMetadata.isAccessPermitted(any());
+
+			assertThat(result).isTrue();
+		}
+
+		@Test
+		void shouldReturnFalseIfNotGranted() {
+			when(decision.isGranted()).thenReturn(false);
+
+			var result = delegatingResourceMetadata.isAccessPermitted(any());
+
+			assertThat(result).isFalse();
+		}
+
+		@Test
+		void shouldCallAuthManager() {
+			delegatingResourceMetadata.isAccessPermitted(Object.class);
+
+			verify(authManager).check(any(), eq(invocation));
+		}
+
+		@Test
+		void shouldReturnTrueIfNotSecured() {
+			when(authManager.check(any(), any())).thenReturn(null);
+
+			var result = delegatingResourceMetadata.isAccessPermitted(any());
+
+			assertThat(result).isTrue();
+		}
+	}
+
+	@Nested
+	class TestGetFindAllInvocation {
+
+		@Mock
+		private MongoRepository<String, Object> repository;
+
+		@Test
+		void shouldReturnMethod() {
+			var method = delegatingResourceMetadata.getFindAllInvocation(repository);
+
+			assertThat(method).isNotNull()
+					.extracting(invocation -> invocation.getMethod().getName()).isEqualTo("findAll");
+		}
+	}
+
+}
diff --git a/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java b/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java
index 0427eb6fbed96cef7143a875cb42d6510654c6ce..d3933e7611bc6c2682d105a3bd92f022f14421a2 100644
--- a/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java
+++ b/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java
@@ -25,6 +25,7 @@ package de.ozgcloud.admin;
 
 import static de.ozgcloud.admin.RootModelAssembler.*;
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
 
 import java.util.List;
 
@@ -34,17 +35,16 @@ import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.Spy;
 import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties;
 import org.springframework.hateoas.Link;
 
 import de.ozgcloud.admin.common.user.CurrentUserService;
-import de.ozgcloud.admin.common.user.UserRole;
 
 class RootModelAssemblerTest {
 
 	private static final String BASE_PATH = "/api/base";
+
 	@Spy
 	@InjectMocks
 	private RootModelAssembler modelAssembler;
@@ -57,17 +57,17 @@ class RootModelAssemblerTest {
 	@DisplayName("Entity Model")
 	@Nested
 	class TestEntityModel {
+
+		private final List<Link> links = List.of(Link.of(RootController.PATH));
+
 		@BeforeEach
 		void beforeEach() {
-			Mockito.when(currentUserService.hasRole(UserRole.ADMIN_ADMIN)).thenReturn(true);
-			Mockito.when(restProperties.getBasePath()).thenReturn(BASE_PATH);
+			doReturn(links).when(modelAssembler).buildRootModelLinks();
 		}
 
 		@Test
 		void shouldHaveRoot() {
 			var givenRoot = RootTestFactory.create();
-			List<Link> links = List.of();
-			Mockito.when(modelAssembler.buildRootModelLinks()).thenReturn(links);
 
 			var resultRoot = modelAssembler.toModel(givenRoot).getContent();
 
@@ -76,9 +76,6 @@ class RootModelAssemblerTest {
 
 		@Test
 		void shouldHaveLinks() {
-			List<Link> links = List.of(Link.of(RootController.PATH));
-			Mockito.when(modelAssembler.buildRootModelLinks()).thenReturn(links);
-
 			var modelLinks = modelAssembler.toModel(RootTestFactory.create()).getLinks();
 
 			assertThat(modelLinks).containsAll(links);
@@ -90,26 +87,48 @@ class RootModelAssemblerTest {
 	class TestBuildRootModelLinks {
 
 		@Test
-		void shouldHaveHrefToBasePathIfAuthorized() {
-			Mockito.when(restProperties.getBasePath()).thenReturn(BASE_PATH);
-			Mockito.when(currentUserService.hasRole(UserRole.ADMIN_ADMIN)).thenReturn(true);
+		void shouldCheckConfigurationPermission() {
+			modelAssembler.buildRootModelLinks();
+
+			verify(currentUserService).hasConfigurationPermission();
+		}
 
-			List<Link> links = modelAssembler.buildRootModelLinks();
+		@Nested
+		class TestOnHasConfigurationPermission {
 
-			assertThat(links).containsExactly(
-					Link.of(RootController.PATH),
-					Link.of(BASE_PATH, REL_CONFIGURATION));
+			@BeforeEach
+			void hasConfigurationPermission() {
+				when(currentUserService.hasConfigurationPermission()).thenReturn(true);
+				when(restProperties.getBasePath()).thenReturn(BASE_PATH);
+			}
+
+			@Test
+			void shouldHaveHrefToConfiguration() {
+				var links = modelAssembler.buildRootModelLinks();
+
+				assertThat(links).containsExactly(
+						Link.of(RootController.PATH),
+						Link.of(BASE_PATH, REL_CONFIGURATION));
+			}
 		}
 
-		@Test
-		void shouldNotHaveHrefToBasePathIfUnauthorized() {
-			Mockito.when(currentUserService.hasRole(UserRole.ADMIN_ADMIN)).thenReturn(false);
+		@Nested
+		class TestOnNotHasConfigurationPermission {
 
-			List<Link> links = modelAssembler.buildRootModelLinks();
+			@BeforeEach
+			void hasNotConfigurationPermission() {
+				when(currentUserService.hasConfigurationPermission()).thenReturn(false);
+			}
 
-			assertThat(links).containsExactly(
-					Link.of(RootController.PATH));
+			@Test
+			void shouldHaveOnlySelfLink() {
+				var links = modelAssembler.buildRootModelLinks();
+
+				assertThat(links).containsExactly(
+						Link.of(RootController.PATH));
+			}
 		}
+
 	}
 
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/common/CollectionModelBuilderTest.java b/src/test/java/de/ozgcloud/admin/common/CollectionModelBuilderTest.java
deleted file mode 100644
index 8327c18845e9ead36fc76bfff226334adac98881..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/admin/common/CollectionModelBuilderTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import static org.assertj.core.api.Assertions.*;
-
-import java.util.List;
-import java.util.stream.Stream;
-
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.springframework.hateoas.Link;
-
-import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheit;
-import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitTestFactory;
-
-class CollectionModelBuilderTest {
-
-	private final String HREF = "http://test";
-	private final String REL = "rel";
-
-	@Nested
-	class TestBuildModel {
-
-		@Test
-		void shouldBuildModel() {
-			var vorgang = OrganisationsEinheitTestFactory.create();
-
-			var model = CollectionModelBuilder.fromEntities(List.of(vorgang)).buildModel();
-
-			assertThat(model.getContent()).hasSize(1).first().usingRecursiveComparison().isEqualTo(vorgang);
-		}
-	}
-
-	@Nested
-	class TestAddLink {
-
-		@Test
-		void shouldAddLink() {
-			var model = CollectionModelBuilder.fromEntities(List.of()).addLink(Link.of(HREF, REL)).buildModel();
-
-			assertThat(model.getLinks()).hasSize(1).first().extracting(Link::getHref, link -> link.getRel().value()).containsExactly(HREF, REL);
-		}
-	}
-
-	@Nested
-	class TestIfMatch {
-
-		@Nested
-		class TestWithBooleanSupplier {
-
-			@Test
-			void shouldAddLink() {
-				var model = CollectionModelBuilder.fromEntities(List.of()).ifMatch(() -> true).addLink(Link.of(HREF, REL)).buildModel();
-
-				assertThat(model.getLinks()).hasSize(1).first().extracting(Link::getHref, link -> link.getRel().value()).containsExactly(HREF, REL);
-			}
-
-			@Test
-			void shouldNotAddLink() {
-				var model = CollectionModelBuilder.fromEntities(List.of()).ifMatch(() -> false).addLink(Link.of(HREF, REL)).buildModel();
-
-				assertThat(model.getLinks()).isEmpty();
-			}
-		}
-
-		@Nested
-		class TestWithPredicate {
-
-			private final Stream<OrganisationsEinheit> wiedervorlageStream = Stream.of(OrganisationsEinheitTestFactory.create());
-
-			@Test
-			void shouldAddLink() {
-				var model = CollectionModelBuilder.fromEntities(wiedervorlageStream)
-						.ifMatch(wiedervorlagen -> true)
-						.addLink(Link.of(HREF, REL))
-						.buildModel();
-
-				assertThat(model.getLinks()).hasSize(1).first().extracting(Link::getHref, link -> link.getRel().value()).containsExactly(HREF, REL);
-			}
-
-			@Test
-			void shouldNotAddLink() {
-				var model = CollectionModelBuilder.fromEntities(wiedervorlageStream)
-						.ifMatch(wiedervorlagen -> false)
-						.addLink(Link.of(HREF, REL))
-						.buildModel();
-
-				assertThat(model.getLinks()).isEmpty();
-			}
-		}
-	}
-
-}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/common/IdBuilderTest.java b/src/test/java/de/ozgcloud/admin/common/IdBuilderTest.java
deleted file mode 100644
index 608fe7e6e3607c04bdcedca85b29238bd840db66..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/admin/common/IdBuilderTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import com.fasterxml.jackson.databind.BeanProperty;
-
-class IdBuilderTest {
-
-	@DisplayName("Test building ID when deserializing linked resources")
-	@Nested
-	class TestBuilingId {
-		private static final String ID = "id";
-
-		@Test
-		void shouldBuildId() {
-			IdBuilder idBuilder = new IdBuilder();
-
-			var idObject = idBuilder.build(ID);
-
-			assertThat(idObject).isInstanceOf(Object.class).asString().isEqualTo(ID);
-		}
-
-		@Test
-		void shouldCreateObjectBuilder() {
-			BeanProperty property = mock(BeanProperty.class);
-			ObjectBuilder<Object> idBuilder = new IdBuilder().constructContextAware(property);
-
-			assertThat(idBuilder).isNotNull();
-		}
-	}
-}
diff --git a/src/test/java/de/ozgcloud/admin/common/LinkedResourceDeserializerTest.java b/src/test/java/de/ozgcloud/admin/common/LinkedResourceDeserializerTest.java
deleted file mode 100644
index 57b29873233059f02882fe1b56c5f4079f57c792..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/admin/common/LinkedResourceDeserializerTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import static org.assertj.core.api.Assertions.*;
-
-import java.io.IOException;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import com.fasterxml.jackson.core.exc.StreamReadException;
-import com.fasterxml.jackson.databind.DatabindException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import de.ozgcloud.admin.common.user.TestId;
-import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitTestFactory;
-
-class LinkedResourceDeserializerTest {
-	private static final String TEST_JSON = "{\"id\":\"/api/vorgangs/" + OrganisationsEinheitTestFactory.ID + "\"}";
-
-	@DisplayName("Test the deserilization of linked resource json")
-	@Nested
-	class TestDeserialization {
-		@Test
-		void shouldDeserialize() throws IOException {
-			LinkedResourceTestObject res = new ObjectMapper().readValue(TEST_JSON.getBytes(), LinkedResourceTestObject.class);
-
-			assertThat(res).hasFieldOrPropertyWithValue("id", TestId.from(OrganisationsEinheitTestFactory.ID));
-		}
-
-	}
-
-}
diff --git a/src/test/java/de/ozgcloud/admin/common/LinkedResourceSerializerTest.java b/src/test/java/de/ozgcloud/admin/common/LinkedResourceSerializerTest.java
deleted file mode 100644
index 05764d09b88e72edd2032e861bbc58cd863a36c9..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/admin/common/LinkedResourceSerializerTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-import static org.assertj.core.api.Assertions.*;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import de.ozgcloud.admin.common.user.TestId;
-import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitTestFactory;
-
-class LinkedResourceSerializerTest {
-
-	@DisplayName("Test the json serialization of linked resource annotations")
-	@Nested
-	class TestSerialization {
-		@Test
-		void shouldSerialize() throws JsonProcessingException {
-			var testObj = new LinkedResourceTestObject(TestId.from(OrganisationsEinheitTestFactory.ID));
-
-			String serialized = new ObjectMapper().writeValueAsString(testObj);
-
-			assertThat(serialized).isEqualTo("{\"id\":\"/api/organisationseinheits/" + OrganisationsEinheitTestFactory.ID + "\"}");
-		}
-
-	}
-}
diff --git a/src/test/java/de/ozgcloud/admin/common/ModelBuilderTest.java b/src/test/java/de/ozgcloud/admin/common/ModelBuilderTest.java
deleted file mode 100644
index eaaf4500527625ff9707498335e93fb6bc6210a3..0000000000000000000000000000000000000000
--- a/src/test/java/de/ozgcloud/admin/common/ModelBuilderTest.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
- * Ministerpräsidenten des Landes Schleswig-Holstein
- * Staatskanzlei
- * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
- *
- * Lizenziert unter der EUPL, Version 1.2 oder - sobald
- * diese von der Europäischen Kommission genehmigt wurden -
- * Folgeversionen der EUPL ("Lizenz");
- * Sie dürfen dieses Werk ausschließlich gemäß
- * dieser Lizenz nutzen.
- * Eine Kopie der Lizenz finden Sie hier:
- *
- * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
- *
- * Sofern nicht durch anwendbare Rechtsvorschriften
- * gefordert oder in schriftlicher Form vereinbart, wird
- * die unter der Lizenz verbreitete Software "so wie sie
- * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
- * ausdrücklich oder stillschweigend - verbreitet.
- * Die sprachspezifischen Genehmigungen und Beschränkungen
- * unter der Lizenz sind dem Lizenztext zu entnehmen.
- */
-package de.ozgcloud.admin.common;
-
-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.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.NullAndEmptySource;
-import org.mockito.Mock;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.env.Environment;
-import org.springframework.web.bind.annotation.RequestMapping;
-
-import lombok.Builder;
-
-class ModelBuilderTest {
-
-	@DisplayName("Add link by annotation if missing")
-	@Nested
-	class TestAddLinkByAnnotationIfMissing {
-
-		private static final String USER_MANAGER_URL = "http://localhost";
-		private static final String USER_MANAGER_PROFILE_TEMPLATE = "/api/profile/%s";
-
-		@Mock
-		private ApplicationContext context;
-
-		private TestEntity entity = TestEntityTestFactory.create();
-
-		@Test
-		void shouldHaveAddLinkByLinkedResource() {
-			var model = ModelBuilder.fromEntity(entity).buildModel();
-
-			assertThat(model.getLink(TestController.FILE_REL).get().getHref()).isEqualTo(TestController.PATH + "/" + TestEntityTestFactory.FILE);
-		}
-
-		@ParameterizedTest
-		@NullAndEmptySource
-		void shouldNotAddLinkByLinkedRessourceIfFieldValueIsNotSet(String fileId) {
-			var model = ModelBuilder.fromEntity(TestEntityTestFactory.createBuilder().file(fileId).build()).buildModel();
-
-			assertThat(model.getLink(TestController.FILE_REL)).isEmpty();
-		}
-	}
-}
-
-@Builder
-class TestEntity {
-
-	@LinkedResource(controllerClass = TestController.class)
-	private String file;
-}
-
-@RequestMapping(TestController.PATH)
-class TestController {
-
-	static final String PATH = "/api/test";
-
-	static final String USER_REL = "user";
-	static final String FILE_REL = "file";
-
-}
-
-class TestEntityTestFactory {
-
-	static final String USER = UUID.randomUUID().toString();
-	static final String FILE = UUID.randomUUID().toString();
-
-	public static TestEntity create() {
-		return createBuilder().build();
-	}
-
-	public static TestEntity.TestEntityBuilder createBuilder() {
-		return TestEntity.builder()
-				.file(FILE);
-	}
-}
diff --git a/src/test/java/de/ozgcloud/admin/common/user/CurrentUserHelperTest.java b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserHelperTest.java
index 46780c05d9f9325f47601d1caad303bb53aba19a..1c171505ed751a6e5e4ecc0d9ba72d06f93b1f01 100644
--- a/src/test/java/de/ozgcloud/admin/common/user/CurrentUserHelperTest.java
+++ b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserHelperTest.java
@@ -24,9 +24,11 @@
 package de.ozgcloud.admin.common.user;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
 
-import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -35,7 +37,6 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 import org.mockito.Mock;
 import org.mockito.MockedStatic;
-import org.mockito.Mockito;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -43,23 +44,58 @@ import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.User;
 
+import com.thedeanda.lorem.LoremIpsum;
+
 class CurrentUserHelperTest {
 	@DisplayName("Has role")
 	@Nested
 	class TestHasRole {
 		@Mock
-		private final Authentication mockAuthentication = Mockito.mock(Authentication.class);
+		private Authentication mockAuthentication;
+		@Mock
+		private User mockPrincipal;
+
+		private final String role = LoremIpsum.getInstance().getWords(1);
+
+		@Test
+		void shouldCallHasAnyRole() {
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class, CALLS_REAL_METHODS)) {
+				mockUserHelper.when(() -> CurrentUserHelper.hasAnyRole(any())).thenReturn(false);
+
+				CurrentUserHelper.hasRole(role);
+
+				mockUserHelper.verify(() -> CurrentUserHelper.hasAnyRole(Set.of(role)));
+			}
+		}
+
+		@ParameterizedTest
+		@ValueSource(booleans = { false, true })
+		void shouldReturnValue(boolean hasRole) {
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class, CALLS_REAL_METHODS)) {
+				mockUserHelper.when(() -> CurrentUserHelper.hasAnyRole(any())).thenReturn(hasRole);
+
+				var result = CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN);
+
+				assertThat(result).isEqualTo(hasRole);
+			}
+		}
+	}
+
+	@Nested
+	class TestHasAnyRole {
+		@Mock
+		private Authentication mockAuthentication;
 		@Mock
-		private final User mockPrincipal = Mockito.mock(User.class);
+		private User mockPrincipal;
+
+		private final Set<String> roles = Set.of(LoremIpsum.getInstance().getWords(1));
 
 		@Test
 		void shouldReturnFalseOnMissingAuthentication() {
-			try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic(
-					CurrentUserHelper.class,
-					Mockito.CALLS_REAL_METHODS)) {
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class)) {
 				mockUserHelper.when(CurrentUserHelper::getAuthentication).thenReturn(null);
 
-				boolean hasRole = CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN);
+				boolean hasRole = CurrentUserHelper.hasAnyRole(roles);
 
 				assertThat(hasRole).isFalse();
 			}
@@ -67,66 +103,72 @@ class CurrentUserHelperTest {
 
 		@Test
 		void shouldReturnFalseOnMissingPrincipal() {
-			Mockito.when(mockAuthentication.getPrincipal()).thenReturn(null);
-			try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic(
-					CurrentUserHelper.class,
-					Mockito.CALLS_REAL_METHODS)) {
+			when(mockAuthentication.getPrincipal()).thenReturn(null);
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class, CALLS_REAL_METHODS)) {
 				mockUserHelper.when(CurrentUserHelper::getAuthentication).thenReturn(mockAuthentication);
 
-				boolean hasRole = CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN);
+				boolean hasRole = CurrentUserHelper.hasAnyRole(roles);
 
 				assertThat(hasRole).isFalse();
 			}
 		}
 
+		@Test
+		void shouldCallContainsAnyRole() {
+			when(mockAuthentication.getPrincipal()).thenReturn(mockPrincipal);
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class, CALLS_REAL_METHODS)) {
+				mockUserHelper.when(CurrentUserHelper::getAuthentication).thenReturn(mockAuthentication);
+
+				CurrentUserHelper.hasAnyRole(roles);
+
+				mockUserHelper.verify(() -> CurrentUserHelper.containsAnyRole(mockAuthentication.getAuthorities(), roles));
+			}
+		}
+
 		@ParameterizedTest
-		@ValueSource(booleans = {false, true})
-		void shouldReturnValue(boolean containsRoleValue) {
-			Mockito.when(mockAuthentication.getPrincipal()).thenReturn(mockPrincipal);
-			List<GrantedAuthority> authorities = List.of();
-			Mockito.<Collection<? extends GrantedAuthority>>when(mockAuthentication.getAuthorities()).thenReturn(authorities);
-
-			try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic(
-					CurrentUserHelper.class,
-					Mockito.CALLS_REAL_METHODS)) {
+		@ValueSource(booleans = { false, true })
+		void shouldReturnValue(boolean hasRole) {
+			when(mockAuthentication.getPrincipal()).thenReturn(mockPrincipal);
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class, CALLS_REAL_METHODS)) {
 				mockUserHelper.when(CurrentUserHelper::getAuthentication).thenReturn(mockAuthentication);
-				mockUserHelper.when(() -> CurrentUserHelper.containsRole(Mockito.anyList(), Mockito.anyString()))
-						.thenReturn(containsRoleValue);
+				mockUserHelper.when(() -> CurrentUserHelper.containsAnyRole(any(), any())).thenReturn(hasRole);
 
-				boolean hasRole = CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN);
+				var result = CurrentUserHelper.hasAnyRole(roles);
 
-				mockUserHelper.verify(() -> CurrentUserHelper.containsRole(mockAuthentication.getAuthorities(), UserRole.ADMIN_ADMIN));
-				assertThat(hasRole).isEqualTo(containsRoleValue);
+				assertThat(result).isEqualTo(hasRole);
 			}
 		}
 	}
 
-	@DisplayName("Contains role")
 	@Nested
-	class TestContainsRole {
+	class TestContainsAnyRole {
 		@Test
-		void shouldNotContainRoleIfAuthoritiesIsNull() {
-			boolean containsRole = CurrentUserHelper.containsRole(null, UserRole.ADMIN_ADMIN);
+		void shouldReturnFalseIfAuthoritiesIsNull() {
+			boolean containsRole = CurrentUserHelper.containsAnyRole(null, Set.of(LoremIpsum.getInstance().getWords(1)));
 
 			assertThat(containsRole).isFalse();
 		}
 
 		@Test
-		void shouldNotContainRole() {
+		void shouldFalseOnNoMatchingRole() {
+			var userRole = LoremIpsum.getInstance().getWords(1);
+			var rolesToCheck = Set.of(LoremIpsum.getInstance().getWords(1), LoremIpsum.getInstance().getWords(1));
 			List<GrantedAuthority> authorities = List.of(
-					new SimpleGrantedAuthority(CurrentUserHelper.ROLE_PREFIX + "OTHER"));
+					new SimpleGrantedAuthority(CurrentUserHelper.ROLE_PREFIX + userRole));
 
-			boolean containsRole = CurrentUserHelper.containsRole(authorities, UserRole.ADMIN_ADMIN);
+			boolean containsRole = CurrentUserHelper.containsAnyRole(authorities, rolesToCheck);
 
 			assertThat(containsRole).isFalse();
 		}
 
 		@Test
-		void shouldContainRole() {
-			Collection<? extends GrantedAuthority> authorities = List.of(
-					new SimpleGrantedAuthority(CurrentUserHelper.ROLE_PREFIX + UserRole.ADMIN_ADMIN));
+		void shouldReturnTrueOnMatchingRole() {
+			var userRole = LoremIpsum.getInstance().getWords(1);
+			var rolesToCheck = Set.of(LoremIpsum.getInstance().getWords(1), userRole);
+			List<GrantedAuthority> authorities = List.of(
+					new SimpleGrantedAuthority(CurrentUserHelper.ROLE_PREFIX + userRole));
 
-			boolean containsRole = CurrentUserHelper.containsRole(authorities, UserRole.ADMIN_ADMIN);
+			boolean containsRole = CurrentUserHelper.containsAnyRole(authorities, rolesToCheck);
 
 			assertThat(containsRole).isTrue();
 		}
@@ -166,13 +208,13 @@ class CurrentUserHelperTest {
 	@Nested
 	class TestGetAuthentication {
 		@Mock
-		private final SecurityContext mockSecurityContext = Mockito.mock(SecurityContext.class);
+		private final SecurityContext mockSecurityContext = mock(SecurityContext.class);
 
 		@Test
 		void shouldThrowIfNoAuthenticatedUser() {
-			Mockito.when(mockSecurityContext.getAuthentication()).thenReturn(null);
+			when(mockSecurityContext.getAuthentication()).thenReturn(null);
 
-			try (MockedStatic<SecurityContextHolder> contextHolder = Mockito.mockStatic(SecurityContextHolder.class)) {
+			try (MockedStatic<SecurityContextHolder> contextHolder = mockStatic(SecurityContextHolder.class)) {
 				contextHolder.when(SecurityContextHolder::getContext).thenReturn(mockSecurityContext);
 
 				assertThatIllegalStateException()
@@ -183,10 +225,10 @@ class CurrentUserHelperTest {
 
 		@Test
 		void shouldPassAuthentication() {
-			Authentication mockAuthentication = Mockito.mock(Authentication.class);
-			Mockito.when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication);
+			Authentication mockAuthentication = mock(Authentication.class);
+			when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication);
 
-			try (MockedStatic<SecurityContextHolder> contextHolder = Mockito.mockStatic(SecurityContextHolder.class)) {
+			try (MockedStatic<SecurityContextHolder> contextHolder = mockStatic(SecurityContextHolder.class)) {
 				contextHolder.when(SecurityContextHolder::getContext).thenReturn(mockSecurityContext);
 
 				Authentication authentication = CurrentUserHelper.getAuthentication();
diff --git a/src/test/java/de/ozgcloud/admin/common/user/CurrentUserServiceTest.java b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserServiceTest.java
index 09f5132426aa7b5850be192689b12cfb2cac6608..0627188e3cc55fd68cd3079e4cedb4d029f3c917 100644
--- a/src/test/java/de/ozgcloud/admin/common/user/CurrentUserServiceTest.java
+++ b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserServiceTest.java
@@ -23,14 +23,16 @@
  */
 package de.ozgcloud.admin.common.user;
 
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 import org.mockito.MockedStatic;
-import org.mockito.Mockito;
-
-import static org.assertj.core.api.Assertions.assertThat;
 
 class CurrentUserServiceTest {
 	private final CurrentUserService currentUserService = new CurrentUserService();
@@ -39,12 +41,10 @@ class CurrentUserServiceTest {
 	@Nested
 	class TestHasRole {
 		@ParameterizedTest
-		@ValueSource(booleans = {false, true})
+		@ValueSource(booleans = { false, true })
 		void shouldReturnValue(boolean hasRoleValue) {
-			try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic(
-					CurrentUserHelper.class)
-			){
-				mockUserHelper.when(() -> CurrentUserHelper.hasRole(Mockito.anyString()))
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class)) {
+				mockUserHelper.when(() -> CurrentUserHelper.hasRole(anyString()))
 						.thenReturn(hasRoleValue);
 
 				boolean hasRole = currentUserService.hasRole(UserRole.ADMIN_ADMIN);
@@ -54,4 +54,29 @@ class CurrentUserServiceTest {
 			}
 		}
 	}
+
+	@Nested
+	class TestHasConfigurationPermission {
+
+		@Test
+		void shouldCallCurrentUserHelper() {
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class)) {
+				currentUserService.hasConfigurationPermission();
+
+				mockUserHelper.verify(() -> CurrentUserHelper.hasAnyRole(UserRole.CONFIGURATION_ROLES));
+			}
+		}
+
+		@ParameterizedTest
+		@ValueSource(booleans = { false, true })
+		void shouldReturnValue(boolean hasConfigurationPermission) {
+			try (MockedStatic<CurrentUserHelper> mockUserHelper = mockStatic(CurrentUserHelper.class)) {
+				mockUserHelper.when(() -> CurrentUserHelper.hasAnyRole(any())).thenReturn(hasConfigurationPermission);
+
+				var result = currentUserService.hasConfigurationPermission();
+
+				assertThat(result).isEqualTo(hasConfigurationPermission);
+			}
+		}
+	}
 }
diff --git a/src/test/java/de/ozgcloud/admin/environment/FeaturesMapperTest.java b/src/test/java/de/ozgcloud/admin/environment/FeaturesMapperTest.java
index 30742c8a4a0cf0e14822d100f2f7e4e4faff7ddf..afe42562472b9828cbec5d7316c90b76ad3ceabf 100644
--- a/src/test/java/de/ozgcloud/admin/environment/FeaturesMapperTest.java
+++ b/src/test/java/de/ozgcloud/admin/environment/FeaturesMapperTest.java
@@ -1,3 +1,26 @@
+/*
+ * 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.admin.environment;
 
 import static org.assertj.core.api.Assertions.*;
diff --git a/src/test/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessorITCase.java b/src/test/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessorITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..52b0d04414085b50de8397ba2db9d32838c20765
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessorITCase.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 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.admin.keycloak;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
+
+import de.ozgcloud.common.test.ITCase;
+
+class KeyCloakRootProcessorITCase {
+
+	@Nested
+	@SpringBootTest(properties = {
+			"ozgcloud.feature.benutzer-rollen=true"
+	})
+	@ITCase
+	class TestFeatureEnabled {
+
+		@Autowired
+		private ApplicationContext applicationContext;
+
+		@Test
+		void shouldHaveKeyCloakRootProcessorBean() {
+			assertDoesNotThrow(() -> applicationContext.getBean(KeyCloakRootProcessor.class));
+		}
+	}
+
+	@Nested
+	@SpringBootTest(properties = {
+			"ozgcloud.feature.benutzer-rollen=false"
+	})
+	@ITCase
+	class TestFeatureDisabled {
+
+		@Autowired
+		private ApplicationContext applicationContext;
+
+		@Test
+		void shouldHaveKeyCloakRootProcessorBean() {
+			assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(KeyCloakRootProcessor.class));
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessorTest.java b/src/test/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..604e2281fe3b99b0f976988221f7b0d57bb2afe1
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/keycloak/KeyCloakRootProcessorTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2025 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.admin.keycloak;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+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.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.Link;
+
+import com.thedeanda.lorem.LoremIpsum;
+
+import de.ozgcloud.admin.Root;
+import de.ozgcloud.admin.RootTestFactory;
+import de.ozgcloud.admin.common.user.CurrentUserService;
+import de.ozgcloud.admin.common.user.UserRole;
+
+class KeyCloakRootProcessorTest {
+
+	@InjectMocks
+	@Spy
+	private KeyCloakRootProcessor processor;
+	@Mock
+	private CurrentUserService currentUserService;
+	@Mock
+	private KeycloakApiProperties keycloakApiProperties;
+
+	@Nested
+	class TestProcess {
+		private final String href = LoremIpsum.getInstance().getUrl();
+
+		@Test
+		void shouldCheckUserRole() {
+			processModel();
+
+			verify(currentUserService).hasRole(UserRole.ADMIN_ADMIN);
+		}
+
+		@Nested
+		class TestOnAdminRole {
+
+			@BeforeEach
+			void givenHasAdminRole() {
+				when(currentUserService.hasRole(anyString())).thenReturn(true);
+				doReturn(href).when(processor).buildUsersHref();
+			}
+
+			@Test
+			void shouldCallBuildUsersHref() {
+				processModel();
+
+				verify(processor).buildUsersHref();
+			}
+
+			@Test
+			void shouldAddUsersLink() {
+				var model = processModel();
+
+				assertThat(model.getLink(KeyCloakRootProcessor.REL_USERS)).isNotEmpty();
+			}
+
+			@Test
+			void shouldSetHref() {
+				var model = processModel();
+
+				assertThat(model.getLink(KeyCloakRootProcessor.REL_USERS))
+						.get()
+						.extracting(Link::getHref)
+						.isEqualTo(href);
+			}
+		}
+
+		@Nested
+		class TestOnWrongUserRole {
+
+			@BeforeEach
+			void givenHasWrongRole() {
+				when(currentUserService.hasRole(anyString())).thenReturn(false);
+			}
+
+			@Test
+			void shouldNotAddAnyLinks() {
+				var model = processModel();
+
+				assertThat(model.getLinks()).isEmpty();
+			}
+		}
+
+		private EntityModel<Root> processModel() {
+			return processor.process(EntityModel.of(RootTestFactory.create()));
+		}
+	}
+
+	@Nested
+	class TestBuildUsersHref {
+		private final String baseUrl = "https://keycloak.domain.de";
+		private final String realm = LoremIpsum.getInstance().getWords(1);
+
+		@BeforeEach
+		void mockProperties() {
+			when(keycloakApiProperties.getUrl()).thenReturn(baseUrl);
+			when(keycloakApiProperties.getRealm()).thenReturn(realm);
+		}
+
+		@Test
+		void shouldGetKeyCloakUrl() {
+			processor.buildUsersHref();
+
+			verify(keycloakApiProperties).getUrl();
+		}
+
+		@Test
+		void shouldGetKeyCloakRealm() {
+			processor.buildUsersHref();
+
+			verify(keycloakApiProperties).getRealm();
+		}
+
+		@Test
+		void shouldReturnApiEndpoint() {
+			var uriString = processor.buildUsersHref();
+
+			assertThat(uriString).isEqualTo(baseUrl + "/admin/realms/" + realm + "/users");
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/keycloak/KeycloakApiServiceITCase.java b/src/test/java/de/ozgcloud/admin/keycloak/KeycloakApiServiceITCase.java
index dc560c4ed05543fb219f0d6e98376dbbe6c977e8..8f121650c6aa2d05a7904a7d2097996a91bfb793 100644
--- a/src/test/java/de/ozgcloud/admin/keycloak/KeycloakApiServiceITCase.java
+++ b/src/test/java/de/ozgcloud/admin/keycloak/KeycloakApiServiceITCase.java
@@ -29,6 +29,7 @@ import static org.assertj.core.groups.Tuple.tuple;
 import java.util.List;
 import java.util.Optional;
 
+import org.assertj.core.api.InstanceOfAssertFactories;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.keycloak.representations.idm.GroupRepresentation;
@@ -122,9 +123,9 @@ class KeycloakApiServiceITCase {
 
 		private String getOrganisationsEinheitId(GroupRepresentation group) {
 			var attributes = group.getAttributes();
-			return attributes.containsKey(properties.getOrganisationsEinheitIdKey()) ?
-					attributes.get(properties.getOrganisationsEinheitIdKey()).getFirst() :
-					null;
+			return attributes.containsKey(properties.getOrganisationsEinheitIdKey())
+					? attributes.get(properties.getOrganisationsEinheitIdKey()).getFirst()
+					: null;
 		}
 	}
 
@@ -175,7 +176,7 @@ class KeycloakApiServiceITCase {
 			var groupId = service.addGroup(groupToAdd);
 
 			assertThat(findGroupInKeycloak(groupId)).isPresent().get().extracting(GroupRepresentation::getSubGroups)
-					.asList().isEmpty();
+					.asInstanceOf(InstanceOfAssertFactories.LIST).isEmpty();
 		}
 
 		private GroupRepresentation createUniqueGroupRepresentation(String nameSuffix) {
diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitITCase.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitITCase.java
index 8483a40cc9f385ecc6c48cbcb1a57a58ac89ed60..6d136531fc4b9de0489a0a69a8ec2232a987ba12 100644
--- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitITCase.java
+++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitITCase.java
@@ -25,7 +25,6 @@ package de.ozgcloud.admin.organisationseinheit;
 
 import static org.mockito.Mockito.*;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
 import java.util.List;
@@ -82,22 +81,22 @@ class OrganisationsEinheitITCase {
 		void shouldContainList() {
 			var response = mockMvc.perform(get(PATH)).andExpect(status().isOk());
 
-			response.andDo(print()).andExpect(jsonPath("$._embedded.organisationsEinheitList").isNotEmpty());
+			response.andExpect(jsonPath("$._embedded.organisationsEinheits").isNotEmpty());
 		}
 
 		@SneakyThrows
 		@Test
 		void shouldContainOrganisationsEinheit() {
 			mockMvc.perform(get(PATH))
-					.andExpect(jsonPath("$._embedded.organisationsEinheitList[0].name").value(OrganisationsEinheitTestFactory.NAME))
-					.andExpect(jsonPath("$._embedded.organisationsEinheitList[0].organisationsEinheitId").value(
+					.andExpect(jsonPath("$._embedded.organisationsEinheits[0].name").value(OrganisationsEinheitTestFactory.NAME))
+					.andExpect(jsonPath("$._embedded.organisationsEinheits[0].organisationsEinheitId").value(
 							OrganisationsEinheitTestFactory.ORGANISATIONS_EINHEIT_ID))
 					.andExpect(
-							jsonPath("$._embedded.organisationsEinheitList[0].syncResult").value(OrganisationsEinheitTestFactory.SYNC_RESULT.name()))
-					.andExpect(jsonPath("$._embedded.organisationsEinheitList[0].zufiId").doesNotExist())
-					.andExpect(jsonPath("$._embedded.organisationsEinheitList[0].parentId").doesNotExist())
-					.andExpect(jsonPath("$._embedded.organisationsEinheitList[0].keycloakId").doesNotExist())
-					.andExpect(jsonPath("$._embedded.organisationsEinheitList[0].id").doesNotExist());
+							jsonPath("$._embedded.organisationsEinheits[0].syncResult").value(OrganisationsEinheitTestFactory.SYNC_RESULT.name()))
+					.andExpect(jsonPath("$._embedded.organisationsEinheits[0].zufiId").doesNotExist())
+					.andExpect(jsonPath("$._embedded.organisationsEinheits[0].parentId").doesNotExist())
+					.andExpect(jsonPath("$._embedded.organisationsEinheits[0].keycloakId").doesNotExist())
+					.andExpect(jsonPath("$._embedded.organisationsEinheits[0].id").doesNotExist());
 		}
 	}
 
diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorITCase.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1cbf97a6669f18cd7f6f22b8bd94a4b126e69bb
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorITCase.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 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.admin.organisationseinheit;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
+
+import de.ozgcloud.common.test.ITCase;
+
+class OrganisationsEinheitRootProcessorITCase {
+
+	@Nested
+	@SpringBootTest(properties = {
+			"ozgcloud.feature.organisations-einheiten=true",
+			"ozgcloud.organisation-einheit.zufi-search-url=foo"
+	})
+
+	@ITCase
+	class TestFeatureEnabled {
+
+		@Autowired
+		private ApplicationContext applicationContext;
+
+		@Test
+		void shouldHaveOrganisationsEinheitRootProcessorBean() {
+			assertDoesNotThrow(() -> applicationContext.getBean(OrganisationsEinheitRootProcessor.class));
+		}
+	}
+
+	@Nested
+	@SpringBootTest(properties = { "ozgcloud.feature.organisations-einheiten=false", "ozgcloud.organisation-einheit.zufi-search-url=foo" })
+	@ITCase
+	class TestFeatureDisabled {
+
+		@Autowired
+		private ApplicationContext applicationContext;
+
+		@Test
+		void shouldHaveOrganisationsEinheitRootProcessorBean() {
+			assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(OrganisationsEinheitRootProcessor.class));
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java
index f147d68e37d851e374f7376a2e84c34374c1f271..47323d43651ecfe9be484f7b6c625e8fa68292a2 100644
--- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java
+++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java
@@ -24,8 +24,10 @@
 package de.ozgcloud.admin.organisationseinheit;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
 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.InjectMocks;
@@ -33,61 +35,122 @@ import org.mockito.Mock;
 import org.springframework.hateoas.EntityModel;
 import org.springframework.hateoas.Link;
 
+import com.thedeanda.lorem.LoremIpsum;
+
+import de.ozgcloud.admin.Root;
 import de.ozgcloud.admin.RootTestFactory;
-import de.ozgcloud.admin.common.FeatureToggleProperties;
+import de.ozgcloud.admin.common.user.CurrentUserService;
+import de.ozgcloud.admin.common.user.UserRole;
 
 class OrganisationsEinheitRootProcessorTest {
 
 	@InjectMocks
 	private OrganisationsEinheitRootProcessor organisationsEinheitRootProcessor;
 	@Mock
-	private FeatureToggleProperties featureToggleProperties;
+	private OrganisationEinheitProperties organisationsEinheitProperties;
+	@Mock
+	private CurrentUserService currentUserService;
 
 	@Nested
 	class TestProcess {
 
-		@Nested
-		class OrganisationsEinheitenLinkRelation {
+		private final String zufiSearchUri = LoremIpsum.getInstance().getUrl() + "?searchBy={searchBy}";
 
-			@Test
-			void shouldExistsIfFeatureEnabled() {
-				givenFeatureIsEnabled();
+		@Test
+		void shouldCheckUserRole() {
+			processModel();
 
-				var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create()));
+			verify(currentUserService).hasRole(UserRole.ADMIN_ADMIN);
+		}
+
+		@Nested
+		class TestOnWrongUserRole {
 
-				assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN)).isNotEmpty();
+			@BeforeEach
+			void givenHasWrongRole() {
+				when(currentUserService.hasRole(anyString())).thenReturn(false);
 			}
 
 			@Test
-			void shouldNotExistIfFeatureDisabled() {
-				givenFeatureIsDisabled();
+			void shouldNotAddAnyLinks() {
+				var model = processModel();
 
-				var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create()));
+				assertThat(model.getLinks()).isEmpty();
+			}
+		}
+
+		@Nested
+		class TestOnAdminRole {
 
-				assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN)).isEmpty();
+			@BeforeEach
+			void givenHasAdminRole() {
+				when(currentUserService.hasRole(anyString())).thenReturn(true);
+				when(organisationsEinheitProperties.getZufiSearchUri()).thenReturn(zufiSearchUri);
 			}
 
-			@Test
-			void shouldHaveHref() {
-				givenFeatureIsEnabled();
+			@Nested
+			class TestOrganisationsEinheitenLinkRelation {
 
-				var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create()));
+				@Test
+				void shouldExist() {
+					var model = processModel();
 
-				assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN))
-						.get()
-						.extracting(Link::getHref)
-						.isEqualTo(OrganisationsEinheitController.PATH);
-			}
+					assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATION_EINHEITEN)).isNotEmpty();
+				}
 
-			private void givenFeatureIsEnabled() {
-				when(featureToggleProperties.isOrganisationsEinheiten()).thenReturn(true);
+				@Test
+				void shouldHaveHref() {
+					var model = processModel();
+
+					assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATION_EINHEITEN))
+							.get()
+							.extracting(Link::getHref)
+							.isEqualTo(OrganisationsEinheitController.PATH);
+				}
 			}
 
-			private void givenFeatureIsDisabled() {
-				when(featureToggleProperties.isOrganisationsEinheiten()).thenReturn(false);
+			@Nested
+			class TestSearchOrganisationsEinheitenLink {
+
+				@Test
+				void shouldExistsIfFeatureEnabled() {
+					var model = processModel();
+
+					assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_SEARCH_ORGANISATION_EINHEIT)).isNotEmpty();
+				}
+
+				@Test
+				void shouldGetZufiSearchUri() {
+					processModel();
+
+					verify(organisationsEinheitProperties).getZufiSearchUri();
+
+				}
+
+				@Test
+				void shouldHaveHref() {
+					var model = processModel();
+
+					assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_SEARCH_ORGANISATION_EINHEIT))
+							.get()
+							.extracting(Link::getHref)
+							.isEqualTo(zufiSearchUri);
+				}
+
+				@Test
+				void shouldBeTemplated() {
+					var model = processModel();
+
+					assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_SEARCH_ORGANISATION_EINHEIT))
+							.get()
+							.extracting(Link::isTemplated)
+							.isEqualTo(true);
+				}
 			}
 		}
 
+		private EntityModel<Root> processModel() {
+			return organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create()));
+		}
 	}
-
 }
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingDtoTestFactory.java b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingDtoTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..f0c601d43a316e0456cd6b73acd012535d1d6033
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingDtoTestFactory.java
@@ -0,0 +1,22 @@
+package de.ozgcloud.admin.reporting;
+
+import static de.ozgcloud.admin.reporting.AggregationMappingTestFactory.*;
+
+import de.ozgcloud.admin.reporting.ReportingAggregationManagerSettingDto.AggregationMappingDto;
+import de.ozgcloud.admin.reporting.ReportingAggregationManagerSettingDto.FieldMappingDto;
+import de.ozgcloud.admin.reporting.ReportingAggregationManagerSettingDto.FormIdentifierDto;
+
+public class AggregationMappingDtoTestFactory {
+
+	public static final FormIdentifierDto FORM_IDENTIFIER = FormIdentifierDto.builder().formId(FORM_ID).formEngineName(FORM_ENGINE_NAME).build();
+
+	public static AggregationMappingDto create() {
+		return createBuilder().build();
+	}
+
+	public static AggregationMappingDto.AggregationMappingDtoBuilder createBuilder() {
+		return AggregationMappingDto.builder()
+				.formIdentifier(FORM_IDENTIFIER)
+				.fieldMapping(FieldMappingDto.builder().sourcePath(SOURCE_PATH).targetPath(TARGET_PATH).build());
+	}
+}
diff --git a/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingMapperTest.java b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f98c2ede8306f10acc1d5fd7e75c7da9b0662cd
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingMapperTest.java
@@ -0,0 +1,26 @@
+package de.ozgcloud.admin.reporting;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mapstruct.factory.Mappers;
+import org.mockito.InjectMocks;
+
+class AggregationMappingMapperTest {
+
+	@InjectMocks
+	private AggregationMappingMapper mapper = Mappers.getMapper(AggregationMappingMapper.class);
+
+	@Nested
+	class TestToMapping {
+
+		@Test
+		void shouldMapToDto() {
+			var dto = mapper.toMapping(AggregationMappingTestFactory.create());
+
+			assertThat(dto).usingRecursiveComparison().isEqualTo(AggregationMappingDtoTestFactory.create());
+		}
+	}
+
+}
diff --git a/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingRepositoryITCase.java b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingRepositoryITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..a92bcc6b3e210f27a066f54f3dc28db551f45086
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingRepositoryITCase.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2025 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.admin.reporting;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.security.authorization.AuthorizationDeniedException;
+import org.springframework.security.test.context.support.WithMockUser;
+
+import de.ozgcloud.admin.common.user.UserRole;
+import de.ozgcloud.admin.setting.SettingTestFactory;
+import de.ozgcloud.common.test.DataITCase;
+
+@DataITCase
+class AggregationMappingRepositoryITCase {
+
+	@Autowired
+	private AggregationMappingRepository repository;
+
+	@Autowired
+	private MongoOperations operations;
+
+	@BeforeEach
+	void dropCollection() {
+		operations.dropCollection("settings");
+	}
+
+	@Test
+	@WithMockUser(roles = UserRole.DATENBEAUFTRAGUNG)
+	void shouldLoadOnlyFieldMapping() {
+		operations.save(AggregationMappingTestFactory.create());
+		operations.save(SettingTestFactory.create());
+
+		var result = repository.findAll();
+
+		assertThat(result).hasSize(1);
+	}
+
+	@Test
+	@WithMockUser
+	void shouldThrowExceptionOnMissingRole() {
+		operations.save(AggregationMappingTestFactory.create());
+
+		assertThatThrownBy(() -> repository.findAll()).isInstanceOf(AuthorizationDeniedException.class);
+	}
+
+}
diff --git a/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingTestFactory.java b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d41bec21628058b6a5f84ef2858746b1b348d26
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/reporting/AggregationMappingTestFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2025 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.admin.reporting;
+
+import java.util.UUID;
+
+import de.ozgcloud.admin.reporting.AggregationMapping.FieldMapping;
+import de.ozgcloud.admin.reporting.AggregationMapping.FormIdentifier;
+
+public class AggregationMappingTestFactory {
+
+	public static final String ID = UUID.randomUUID().toString();
+	public static final String FORM_ENGINE_NAME = "A12";
+	public static final String FORM_ID = "42";
+
+	public static final String SOURCE_PATH = "name";
+	public static final String TARGET_PATH = "antragsteller.name";
+
+	public static AggregationMapping create() {
+		return createBuilder().build();
+	}
+
+	public static AggregationMapping.AggregationMappingBuilder createBuilder() {
+		return AggregationMapping.builder()
+				.id(UUID.randomUUID().toString())
+				.formIdentifier(FormIdentifier.builder()
+						.formEngineName(FORM_ENGINE_NAME)
+						.formId(FORM_ID)
+						.build())
+				.mapping(FieldMapping.builder().sourcePath(SOURCE_PATH).targetPath(TARGET_PATH).build());
+	}
+
+}
diff --git a/src/main/java/de/ozgcloud/admin/common/IdBuilder.java b/src/test/java/de/ozgcloud/admin/reporting/ReportingAggregationMAnagerSettingDtoTestFactory.java
similarity index 64%
rename from src/main/java/de/ozgcloud/admin/common/IdBuilder.java
rename to src/test/java/de/ozgcloud/admin/reporting/ReportingAggregationMAnagerSettingDtoTestFactory.java
index f893795312adcde8c17d1c0da58cf1eaa4e5cf26..27dc2735fa90521e76642ad8eda61e74c308db75 100644
--- a/src/main/java/de/ozgcloud/admin/common/IdBuilder.java
+++ b/src/test/java/de/ozgcloud/admin/reporting/ReportingAggregationMAnagerSettingDtoTestFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
+ * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den
  * Ministerpräsidenten des Landes Schleswig-Holstein
  * Staatskanzlei
  * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
@@ -21,22 +21,17 @@
  * Die sprachspezifischen Genehmigungen und Beschränkungen
  * unter der Lizenz sind dem Lizenztext zu entnehmen.
  */
-package de.ozgcloud.admin.common;
+package de.ozgcloud.admin.reporting;
 
-import com.fasterxml.jackson.databind.BeanProperty;
+class ReportingAggregationMAnagerSettingDtoTestFactory {
 
-import lombok.NoArgsConstructor;
-
-@NoArgsConstructor
-class IdBuilder implements ObjectBuilder<Object> {
-
-	@Override
-	public Object build(Object id) {
-		return id;
+	public static ReportingAggregationManagerSettingDto create() {
+		return createBuilder().build();
 	}
 
-	@Override
-	public ObjectBuilder<Object> constructContextAware(BeanProperty property) {
-		return new IdBuilder();
+	public static ReportingAggregationManagerSettingDto.ReportingAggregationManagerSettingDtoBuilder createBuilder() {
+		return ReportingAggregationManagerSettingDto.builder()
+				.aggregationMapping(AggregationMappingDtoTestFactory.create());
 	}
+
 }
diff --git a/src/test/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDtoServiceTest.java b/src/test/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDtoServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..38b5d5e310295cecf06d3fd9d4ff723f14c00d54
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/reporting/ReportingAggregationManagerSettingDtoServiceTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2025 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.admin.reporting;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.ozgcloud.admin.setting.SettingRepository;
+
+class ReportingAggregationManagerSettingDtoServiceTest {
+
+	@Spy
+	@InjectMocks
+	private ReportingAggregationManagerSettingDtoService service;
+
+	@Mock
+	private AggregationMappingMapper mapper;
+	@Mock
+	private SettingRepository repository;
+
+	@Nested
+	class TestMapToDto {
+
+		@BeforeEach
+		void setupMapperMock() {
+			when(mapper.toMapping(any())).thenReturn(AggregationMappingDtoTestFactory.create());
+		}
+
+		@Test
+		void shouldCallMapper() {
+			var mapping = AggregationMappingTestFactory.create();
+			service.mapToDto(Stream.of(mapping));
+
+			verify(mapper).toMapping(mapping);
+		}
+
+		@Test
+		void shouldBuildDto() {
+			var dto = service.mapToDto(Stream.of(AggregationMappingTestFactory.create()));
+
+			assertThat(dto).usingRecursiveComparison().isEqualTo(ReportingAggregationMAnagerSettingDtoTestFactory.create());
+		}
+	}
+
+	@Nested
+	class TestGetDataTransferObject {
+
+		@Captor
+		private ArgumentCaptor<Stream<AggregationMapping>> streamCaptor;
+
+		@BeforeEach
+		void disableMapToDtoFunction() {
+			doReturn(ReportingAggregationMAnagerSettingDtoTestFactory.create()).when(service).mapToDto(any());
+		}
+
+		@Test
+		void shouldCallRepository() {
+			service.getDataTransferObject();
+
+			verify(repository).findByType(AggregationMapping.TYPE_ALIAS, AggregationMapping.class);
+		}
+
+		@Test
+		void shouldCallMapToDto() {
+			AggregationMapping mapping = AggregationMappingTestFactory.create();
+			when(repository.findByType(any(), any())).thenReturn(Stream.of(mapping));
+
+			service.getDataTransferObject();
+
+			verify(service).mapToDto(streamCaptor.capture());
+			assertThat(streamCaptor.getValue()).contains(mapping);
+		}
+	}
+
+}
diff --git a/src/test/java/de/ozgcloud/admin/reporting/ReportingSettingITCase.java b/src/test/java/de/ozgcloud/admin/reporting/ReportingSettingITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f347868585a14067918bae7d96351cde99fb9e6
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/reporting/ReportingSettingITCase.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2025 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.admin.reporting;
+
+import static org.assertj.core.api.Assertions.*;
+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 org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+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.data.mongodb.core.MongoOperations;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+
+import de.ozgcloud.admin.common.user.UserRole;
+import de.ozgcloud.common.test.DataITCase;
+import de.ozgcloud.common.test.TestUtils;
+import lombok.SneakyThrows;
+
+@AutoConfigureMockMvc
+@WithMockUser(roles = UserRole.DATENBEAUFTRAGUNG)
+@DataITCase
+class ReportingSettingITCase {
+
+	@Autowired
+	private MockMvc mockMvc;
+
+	@Autowired
+	private MongoOperations mongoOperations;
+
+	@BeforeEach
+	void clearDatabase() {
+		mongoOperations.dropCollection("settings");
+	}
+
+	@Test
+	@SneakyThrows
+	void shouldHaveLinkToAggregationMapping() {
+		mockMvc.perform(get("/api/configuration"))
+				.andExpect(status().is2xxSuccessful())
+				.andExpect(jsonPath("$._links.aggregationMappings").exists());
+	}
+
+	@Test
+	@SneakyThrows
+	@WithMockUser(roles = UserRole.ADMIN_ADMIN)
+	void shouldNotHaveLinkOnMissingRole() {
+		mockMvc.perform(get("/api/configuration"))
+				.andExpect(status().is2xxSuccessful())
+				.andExpect(jsonPath("$._links.aggregationMappings").doesNotExist());
+	}
+
+	@Test
+	@SneakyThrows
+	void shouldDenyWhileMissingFields() {
+		mockMvc.perform(post("/api/configuration/aggregationMappings").with(csrf())
+				.contentType(MediaType.APPLICATION_JSON).content("{}"))
+				.andExpect(status().isUnprocessableEntity());
+	}
+
+	@Disabled("is returning 500")
+	@Test
+	@SneakyThrows
+	@WithMockUser(roles = UserRole.ADMIN_ADMIN)
+	void shouldNotAllowAddingMapping() {
+		mockMvc.perform(post("/api/configuration/aggregationMappings").with(csrf())
+				.contentType(MediaType.APPLICATION_JSON).content(TestUtils.loadTextFile("reporting/request.json")))
+				.andExpect(status().is(403));
+	}
+
+	@Test
+	@SneakyThrows
+	void shouldAddAggregationMappings() {
+		mockMvc.perform(post("/api/configuration/aggregationMappings").with(csrf())
+				.contentType(MediaType.APPLICATION_JSON).content(TestUtils.loadTextFile("reporting/request.json")))
+				.andExpect(status().isCreated());
+
+		var collection = mongoOperations.getCollection("settings");
+		assertThat(collection.countDocuments()).isEqualTo(1);
+		var mapping = mongoOperations.findAll(AggregationMapping.class).getFirst();
+		assertThat(mapping).usingRecursiveComparison().ignoringFields("id").isEqualTo(AggregationMappingTestFactory.create());
+	}
+
+	@Test
+	@SneakyThrows
+	void shouldListReportings() {
+		mockMvc.perform(get("/api/configuration/aggregationMappings"))
+				.andExpect(status().is2xxSuccessful());
+	}
+
+}
diff --git a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java
index 00e842613b7e62c8d62236a7f589f5e4affe887d..f8a507f213af4b16ae613bfca2560f5422f35ce1 100644
--- a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java
+++ b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java
@@ -35,6 +35,7 @@ import org.junit.jupiter.params.provider.ValueSource;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.http.HttpStatus;
+import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 
@@ -137,15 +138,10 @@ class SecurityConfigurationITCase {
 	@DisplayName("with authentication")
 	@Nested
 	class TestWithAuthentication {
-		static final String CLAIMS = """
-				{
-				  "preferredUsername": "testUser",
-				  "scope": "openid testscope"
-				}""";
 
 		@Test
 		@SneakyThrows
-		@WithJwt(CLAIMS)
+		@WithMockUser
 		void shouldAllowApiEndpoint() {
 			var result = doPerformAuthenticated("/api");
 
@@ -154,8 +150,8 @@ class SecurityConfigurationITCase {
 
 		@Test
 		@SneakyThrows
-		@WithJwt(CLAIMS)
-		void shouldForbidSettingsEndpoint() {
+		@WithMockUser
+		void shouldForbidSettingsEndpointOnMissingRole() {
 			var result = doPerformAuthenticated("/api/configuration/settings");
 
 			result.andExpect(status().isForbidden());
@@ -163,7 +159,7 @@ class SecurityConfigurationITCase {
 
 		@Test
 		@SneakyThrows
-		@WithJwt(CLAIMS)
+		@WithMockUser
 		void shouldForbidConfigurationsEndpoint() {
 			var result = doPerformAuthenticated("/api/configuration");
 
@@ -180,17 +176,9 @@ class SecurityConfigurationITCase {
 	@Nested
 	class TestWithAdminRole {
 
-		static final String CLAIMS = """
-				{
-				  "preferredUsername": "testUser",
-				  "scope": "openid testscope",
-				  "resource_access": { "admin": { "roles": ["ADMIN_ADMIN"] } }
-				}""";
-
-
 		@Test
 		@SneakyThrows
-		@WithJwt(CLAIMS)
+		@WithMockUser(roles = "ADMIN_ADMIN")
 		void shouldAllowSettings() {
 			var result = mockMvc.perform(get("/api/configuration/settings"));
 
@@ -199,7 +187,7 @@ class SecurityConfigurationITCase {
 
 		@Test
 		@SneakyThrows
-		@WithJwt(CLAIMS)
+		@WithMockUser(roles = "ADMIN_ADMIN")
 		void shouldAllowConfiguration() {
 			var result = mockMvc.perform(get("/api/configuration"));
 
diff --git a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationTest.java b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationTest.java
index df88ea5f503807b4b58878d8ced4e09eff353d79..c84999918e651d428abdb93d54e6dff5f0c7eb35 100644
--- a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationTest.java
+++ b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationTest.java
@@ -23,10 +23,10 @@
  */
 package de.ozgcloud.admin.security;
 
-import static de.ozgcloud.admin.security.JwtTestFactory.*;
 import static de.ozgcloud.admin.security.SecurityConfiguration.*;
 import static java.util.Collections.*;
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import java.util.List;
@@ -77,9 +77,8 @@ class SecurityConfigurationTest {
 		@Test
 		void shouldUsePreferredUsername() {
 			var preferredName = LoremIpsum.getInstance().getName();
-			var jwtWithPreferredName = JwtTestFactory.createBuilder()
-					.claim(StandardClaimNames.PREFERRED_USERNAME, preferredName)
-					.build();
+			var jwtWithPreferredName = JwtTestFactory.createBuilder().claim(StandardClaimNames.PREFERRED_USERNAME,
+					preferredName).build();
 
 			var jwtAuthenticationConverter = securityConfiguration.jwtAuthenticationConverter();
 
@@ -109,8 +108,10 @@ class SecurityConfigurationTest {
 
 		@BeforeEach
 		void mock() {
-			var keycloakRoles = List.of(ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3);
-			expectedSecurityRoleStrings = keycloakRoles.stream().map(role -> SIMPLE_GRANT_AUTHORITY_PREFIX + role).toList();
+			var keycloakRoles = List.of(JwtTestFactory.ROLE_1,
+					JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3);
+			expectedSecurityRoleStrings = keycloakRoles.stream().map(role -> SIMPLE_GRANT_AUTHORITY_PREFIX +
+					role).toList();
 			doReturn(keycloakRoles).when(securityConfiguration).getRolesFromJwt(any());
 		}
 
@@ -131,14 +132,14 @@ class SecurityConfigurationTest {
 
 			var grantedAuthorities = securityConfiguration.convertJwtToGrantedAuthorities(jwt);
 
-			var securityRoles = grantedAuthorities
-					.stream()
+			var securityRoles = grantedAuthorities.stream()
 					.map(GrantedAuthority::getAuthority).toList();
 			assertThat(securityRoles).containsAll(expectedSecurityRoleStrings);
 		}
 	}
 
 	@DisplayName("get roles from jwt")
+
 	@Nested
 	class TestGetRolesFromJwt {
 
@@ -158,16 +159,16 @@ class SecurityConfigurationTest {
 
 		private static Stream<Arguments> getIncompleteJwt() {
 			return Stream.of(JwtTestFactory.create(),
-							JwtTestFactory.createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of()).build(),
-							JwtTestFactory.createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of("admin", Map.of())).build(),
-							JwtTestFactory.createWithRoles(emptyList()).build())
+					JwtTestFactory.createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of()).build(),
+					JwtTestFactory.createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of("admin", Map.of())).build(),
+					JwtTestFactory.createWithRoles(emptyList()).build())
 					.map(Arguments::of);
 		}
 
 		@DisplayName("should return resource_access.admin.roles list")
 		@Test
 		void shouldReturnResourceAccessAdminRolesList() {
-			var expectedRoles = List.of(ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3);
+			var expectedRoles = List.of(JwtTestFactory.ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3);
 			var jwtWithRoles = JwtTestFactory.createWithRoles(expectedRoles).build();
 
 			var roleStrings = securityConfiguration.getRolesFromJwt(jwtWithRoles);
@@ -176,4 +177,5 @@ class SecurityConfigurationTest {
 		}
 
 	}
+
 }
diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentITCase.java b/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentITCase.java
index c50d09934b9cfd835215a4589a0c70000fdc9419..4fe44a5e5de623e8c08dfd39cb6073058c497ed5 100644
--- a/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentITCase.java
+++ b/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentITCase.java
@@ -38,6 +38,7 @@ import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.ResultActions;
 
+import de.ozgcloud.admin.common.user.UserRole;
 import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheit;
 import de.ozgcloud.admin.organisationseinheit.OrganisationsEinheitTestFactory;
 import de.ozgcloud.admin.setting.postfach.PostfachSettingBodyTestFactory;
@@ -46,7 +47,7 @@ import lombok.SneakyThrows;
 
 @DataITCase
 @AutoConfigureMockMvc
-@WithMockUser
+@WithMockUser(roles = UserRole.ADMIN_ADMIN)
 class SettingEnvironmentITCase {
 
 	@Autowired
diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingRepositoryITCase.java b/src/test/java/de/ozgcloud/admin/setting/SettingRepositoryITCase.java
index 3fb17b47e79176424afab28bcc63cf082a17ade2..4f7dc9e6b6b543a1c701b5c875cf70244600f442 100644
--- a/src/test/java/de/ozgcloud/admin/setting/SettingRepositoryITCase.java
+++ b/src/test/java/de/ozgcloud/admin/setting/SettingRepositoryITCase.java
@@ -29,31 +29,46 @@ import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.security.test.context.support.WithMockUser;
 
+import de.ozgcloud.admin.common.user.UserRole;
+import de.ozgcloud.admin.reporting.AggregationMappingTestFactory;
 import de.ozgcloud.common.test.DataITCase;
 
 @DataITCase
+@WithMockUser(roles = UserRole.ADMIN_ADMIN)
 class SettingRepositoryITCase {
+
 	@Autowired
 	private MongoOperations mongoOperations;
 	@Autowired
 	private SettingRepository repository;
 
-	private final static String SETTING_NAME = "Name";
-	private Setting setting = SettingTestFactory.createBuilder()
-			.name(SETTING_NAME)
-			.build();
-
 	@Nested
 	class TestFindOneByName {
 		@Test
 		void shouldGetSavedData() {
 			mongoOperations.dropCollection(Setting.COLLECTION_NAME);
-			mongoOperations.save(setting);
+			mongoOperations.save(SettingTestFactory.create());
+
+			var settingFromDb = repository.findOneByName(SettingTestFactory.SETTING_NAME).get();
+
+			assertThat(settingFromDb).usingRecursiveComparison().ignoringFields("id").isEqualTo(SettingTestFactory.create());
+		}
+	}
+
+	@Nested
+	class TestFindByType {
+
+		@Test
+		void shouldReturnOnlySetting() {
+			mongoOperations.dropCollection(Setting.COLLECTION_NAME);
+			mongoOperations.save(SettingTestFactory.create());
+			mongoOperations.save(AggregationMappingTestFactory.create());
 
-			var settingFromDb = repository.findOneByName(SETTING_NAME).get();
+			var loaded = repository.findByType("Setting", Setting.class);
 
-			assertThat(settingFromDb).usingRecursiveComparison().isEqualTo(setting);
+			assertThat(loaded).hasSize(1).first().isInstanceOf(Setting.class);
 		}
 	}
 }
diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/SettingTestFactory.java
index 07e9533f4895b545042a35186f8d96080dd14952..54c4ae3b42514c653e28615ccc8b54e0cbc5f7b3 100644
--- a/src/test/java/de/ozgcloud/admin/setting/SettingTestFactory.java
+++ b/src/test/java/de/ozgcloud/admin/setting/SettingTestFactory.java
@@ -27,12 +27,14 @@ import de.ozgcloud.common.test.TestUtils;
 
 public class SettingTestFactory {
 
+	static final String SETTING_NAME = "Name";
+
 	public static Setting create() {
 		return createBuilder().build();
 	}
 
 	public static Setting.SettingBuilder createBuilder() {
-		return Setting.builder();
+		return Setting.builder().name(SETTING_NAME);
 	}
 
 	public static String buildSettingJson(Setting setting, String settingBodyString) {
diff --git a/src/test/resources/application-itcase.yaml b/src/test/resources/application-itcase.yaml
index 1217c868aaa994671a26171005505ce110d401a5..1361c8ede1a7c0664bb56cdd718ac67942899c4c 100644
--- a/src/test/resources/application-itcase.yaml
+++ b/src/test/resources/application-itcase.yaml
@@ -1,3 +1,9 @@
+logging:
+  level:
+    '[org.springframework.data.mongodb]': WARN
+    '[org.springframework.security]': WARN
+  config: classpath:log4j2-local.xml
+
 spring:
   data:
     mongodb:
diff --git a/src/test/resources/reporting/request.json b/src/test/resources/reporting/request.json
new file mode 100644
index 0000000000000000000000000000000000000000..c2db9de85906742429d7bcd56bc4ae61a28f2580
--- /dev/null
+++ b/src/test/resources/reporting/request.json
@@ -0,0 +1,12 @@
+{
+	"formIdentifier": {
+		"formEngineName": "A12",
+		"formId": "42"
+	},
+	"mappings": [
+		{
+			"sourcePath": "name",
+			"targetPath": "antragsteller.name"
+		}
+	] 
+}
\ No newline at end of file