diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml
index 7817c84c7f6da1e595b076148cc8bdc105cdda08..cdeb4e3624f4f6aa78a0e1a1f6c3cfaa3cb33d20 100644
--- a/.docker/docker-compose.yml
+++ b/.docker/docker-compose.yml
@@ -109,7 +109,7 @@ services:
   dbrepo-auth-service-init:
     init: true
     restart: "no"
-    image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.1
     environment:
       AUTH_SERVICE_ADMIN: ${AUTH_SERVICE_ADMIN:-admin}
       AUTH_SERVICE_ADMIN_PASSWORD: ${AUTH_SERVICE_ADMIN_PASSWORD:-admin}
@@ -130,7 +130,7 @@ services:
     restart: "no"
     container_name: dbrepo-metadata-service
     hostname: metadata-service
-    image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.1
     volumes:
       - "${SHARED_VOLUME:-/tmp}:/tmp"
     environment:
@@ -193,7 +193,7 @@ services:
     restart: "no"
     container_name: dbrepo-analyse-service
     hostname: analyse-service
-    image: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.1
     environment:
       AUTH_SERVICE_CLIENT: ${AUTH_SERVICE_CLIENT:-dbrepo-client}
       AUTH_SERVICE_CLIENT_SECRET: ${AUTH_SERVICE_CLIENT:-MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG}
@@ -248,7 +248,7 @@ services:
     restart: "no"
     container_name: dbrepo-search-db
     hostname: search-db
-    image: registry.datalab.tuwien.ac.at/dbrepo/search-db:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/search-db:1.6.1
     healthcheck:
       test: curl -sSL localhost:9200/_plugins/_security/health | jq .status | grep UP
       interval: 10s
@@ -272,18 +272,20 @@ services:
     restart: "no"
     container_name: dbrepo-search-service
     hostname: search-service
-    image: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.1
     environment:
       AUTH_SERVICE_CLIENT: ${AUTH_SERVICE_CLIENT:-dbrepo-client}
       AUTH_SERVICE_CLIENT_SECRET: ${AUTH_SERVICE_CLIENT_SECRET:-MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG}
       AUTH_SERVICE_ENDPOINT: ${AUTH_SERVICE_ENDPOINT:-http://auth-service:8080}
       COLLECTION: ${COLLECTION:-['database','table','column','identifier','unit','concept','user','view']}
+      LOG_LEVEL: ${LOG_LEVEL:-info}
       METADATA_SERVICE_ENDPOINT: ${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}
       OPENSEARCH_HOST: ${OPENSEARCH_HOST:-search-db}
       OPENSEARCH_PORT: ${OPENSEARCH_PORT:-9200}
       OPENSEARCH_USERNAME: ${SEARCH_DB_USERNAME:-admin}
       OPENSEARCH_PASSWORD: ${SEARCH_DB_PASSWORD:-admin}
-      LOG_LEVEL: ${LOG_LEVEL:-info}
+      SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
+      SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
     healthcheck:
       test: curl -sSL localhost:8080/health | grep 'UP' || exit 1
       interval: 10s
@@ -294,7 +296,7 @@ services:
     restart: "no"
     container_name: dbrepo-ui
     hostname: ui
-    image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.1
     environment:
       NUXT_PUBLIC_API_CLIENT: "${BASE_URL:-http://localhost}"
       NUXT_PUBLIC_API_SERVER: "${BASE_URL:-http://localhost}"
@@ -363,13 +365,16 @@ services:
     init: true
     container_name: dbrepo-search-service-init
     hostname: search-service-init
-    image: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.1
     environment:
+      LOG_LEVEL: ${LOG_LEVEL:-info}
       METADATA_SERVICE_ENDPOINT: ${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}
       OPENSEARCH_HOST: ${OPENSEARCH_HOST:-search-db}
       OPENSEARCH_PORT: ${OPENSEARCH_PORT:-9200}
       OPENSEARCH_USERNAME: ${SEARCH_DB_USERNAME:-admin}
       OPENSEARCH_PASSWORD: ${SEARCH_DB_PASSWORD:-admin}
+      SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
+      SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
     depends_on:
       dbrepo-search-db:
         condition: service_healthy
@@ -417,7 +422,7 @@ services:
     restart: "no"
     container_name: dbrepo-dashboard-service
     hostname: dashboard-service
-    image: registry.datalab.tuwien.ac.at/dbrepo/dashboard-service:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/dashboard-service:1.6.1
     ports:
       - "3000:3000"
     volumes:
@@ -444,7 +449,7 @@ services:
     init: true
     container_name: dbrepo-storage-service-init
     hostname: storage-service-init
-    image: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.1
     environment:
       S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-seaweedfsadmin}
       S3_BUCKET: "${S3_BUCKET:-dbrepo}"
@@ -489,7 +494,7 @@ services:
     restart: "no"
     container_name: dbrepo-data-service
     hostname: data-service
-    image: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.0
+    image: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.1
     volumes:
       - "${SHARED_VOLUME:-/tmp}:/tmp"
     environment:
diff --git a/.docs/.swagger/api.base.yaml b/.docs/.swagger/api.base.yaml
index df4d4f97d02b4a3c379c5c967ee01537ab631e97..fc0a733ccc7e1c4134f8a444c9b6fe08acb7aabb 100644
--- a/.docs/.swagger/api.base.yaml
+++ b/.docs/.swagger/api.base.yaml
@@ -24,7 +24,7 @@ info:
     name: Apache 2.0
     url: https://www.apache.org/licenses/LICENSE-2.0
   title: DBRepo REST API
-  version: 1.6.0
+  version: 1.6.1
 openapi: 3.1.0
 servers:
   - description: Test Instance
diff --git a/.docs/.swagger/api.yaml b/.docs/.swagger/api.yaml
index b99640757acf76ab5b009f68c9b6d8a85d8c183d..dc7a627e743279d6eddf788cc9b1c18411912e50 100644
--- a/.docs/.swagger/api.yaml
+++ b/.docs/.swagger/api.yaml
@@ -16,7 +16,7 @@ info:
     name: Apache 2.0
     url: 'https://www.apache.org/licenses/LICENSE-2.0'
   title: DBRepo REST API
-  version: 1.6.0
+  version: 1.6.1
 servers:
   - description: Test Instance
     url: 'https://test.dbrepo.tuwien.ac.at'
diff --git a/.docs/api/data-service.md b/.docs/api/data-service.md
index 257c68c1957fa697957c9dfc3f6b681693ac3f9d..bc43a5d3631f7a9b4ecd85c64472bd25095a9603 100644
--- a/.docs/api/data-service.md
+++ b/.docs/api/data-service.md
@@ -24,25 +24,32 @@ author: Martin Weise
 
 ## Overview
 
-The Data Service is responsible for inserting AMQP tuples from the Broker Service into the Data DB 
+The Data Service is responsible for inserting AMQP tuples from the Broker Service into the Data DB
 via [Spring AMQP](https://docs.spring.io/spring-amqp/reference/html/). To increase the number of consumers, scale the
 Data Service up.
 
 ## Data Processing
 
-The Data Service uses [Apache Spark](https://spark.apache.org/), a data engine to load data from/into 
+The Data Service uses [Apache Spark](https://spark.apache.org/), a data engine to load data from/into
 the [Data Database](../data-db) with a wide range of open-source connectors. The default deployment uses a local mode of
-embedded processing directly in the service until there exists 
+embedded processing directly in the service until there exists
 a [Bitnami Chart](https://artifacthub.io/packages/helm/bitnami/spark) for Spark 4.
 
 Retrieving data from a subset internally generates a view with the 64-character hash of the query. This view is not
 automatically deleted currently.
 
+## Caching
+
+The Data Service uses [Caffeine](https://github.com/ben-manes/caffeine), a caching solution that is used to temporarily
+cache the connection details from the [Metadata Service](../metadata-service) such that they don't have to be queried
+everytime e.g. a sensor measurement is inserted. By default, this information is stored for 60 minutes. System
+administrators can disable this behavior by setting `CREDENTIAL_CACHE_TIMEOUT=0` (cache is deleted after 0 seconds).
+
 ## Limitations
 
-* Views in DBRepo can only have 63-character length (it is assumed only internal views have the maximum length of 64 
+* Views in DBRepo can only have 63-character length (it is assumed only internal views have the maximum length of 64
   characters).
-* Local mode of embedded processing of Apache Spark directly in the service using 
+* Local mode of embedded processing of Apache Spark directly in the service using
   a [`local[2]`](https://spark.apache.org/docs/latest/#running-the-examples-and-shell) configuration.
 
 !!! question "Do you miss functionality? Do these limitations affect you?"
diff --git a/.docs/concepts/data-visibility.md b/.docs/concepts/data-visibility.md
index e76448ec3eaddb09ecf7d601d37433f641f19751..04f37c6979bd9019d954859535a757c72da4b63e 100644
--- a/.docs/concepts/data-visibility.md
+++ b/.docs/concepts/data-visibility.md
@@ -2,32 +2,34 @@
 author: Martin Weise
 ---
 
-There are several ways to set the visibility of (meta-)data in DBRepo. It is possible to set the data to public/private
-and the schema to be public/private for each database and separately for each table, each view and each subset of a
-database. 
+There are several ways to set the visibility of (meta-)data in DBRepo. It is possible to set the data visibility to
+visible/hidden and the schema to be visible/hidden for each database and separately for each table, each view and each
+subset of a database.
+
+## Visibility
 
 In total there are three possible scenarios:
 
-## Public
+#### Public
 
 !!! info "Possible use-case: data publication supplement to an open-access publication"
 
-Where the database's data and metadata is set to be *public*. This means everything in the database (tables, views,
+Where the database's data and metadata is set to be *visible*. This means everything in the database (tables, views,
 subsets) are visible by anyone from the public.
 
-## Mixed
+#### Private
 
 !!! info "Possible use-case: private sensor measurements with timed embargo"
 
-Where the database's data and metadata is set to be *private*. This means everything in the database (tables, views,
-subsets) are by default not visible by anyone from the public. You can however make specific views that join tables
-and/or filter certain columns and apply a 14-day delay-embargo.
+Where the database's data set to be *hidden* but the schema to be *visible*. This means everything in the database
+(tables, views, subsets) are by default not visible by anyone from the public. You can however make specific views that
+join tables and/or filter certain columns and apply a 14-day delay-embargo.
 
 <figure markdown>
 ![Mirroring statistical properties in Metadata Database and Search Database](../images/private-embargo.svg)
 <figcaption>Figure 1: Public view that joins two private tables and applies a time-embargo</figcaption>
 </figure>
 
-## Private
+#### Draft
 
-!!! info "Possible use-case: data storage for trusted-/virtual research environments"
\ No newline at end of file
+!!! info "Possible use-case: project data storage before publication"
\ No newline at end of file
diff --git a/.docs/index.md b/.docs/index.md
index e3307bd493e33dc0a03ce3fc2a770e726c30cd3f..e16f9f5da6ccc94637fc0c1b56868e4fda84b023 100644
--- a/.docs/index.md
+++ b/.docs/index.md
@@ -14,7 +14,7 @@ author: Martin Weise
 ![Maintainability Rating](./images/maintainability.svg)
 ![Security Rating](./images/security.svg)
 
-Documentation for version: [v1.6.0](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/releases).
+Documentation for version: [v1.6.1](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/releases).
 
 DBRepo is a repository for data in databases that are used from the beginning until the end of a research 
 project supporting data evolution, -citation and -versioning. It implements the query store of the 
diff --git a/.docs/kubernetes.md b/.docs/kubernetes.md
index 232584ee75a51aaa0eff2eff56bac6631406766c..cc16bbe210d1b4ea98cd27ad8c47248bc551bafb 100644
--- a/.docs/kubernetes.md
+++ b/.docs/kubernetes.md
@@ -14,7 +14,7 @@ helm upgrade --install dbrepo \
   -n dbrepo \
   "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo" \
   --values ./values.yaml \
-  --version "1.6.0" \
+  --version "1.6.1" \
   --create-namespace \
   --cleanup-on-fail
 ```
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e71b7ee27f04784778509256cdd427c74ce5cf18..8bf70c0c712a5b76676b1d10d8663ff1f02a4923 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,8 +9,8 @@ variables:
   SONARQUBE_VERSION: "10.0"
   BUN_VERSION: "1.1.40"
   DOC_VERSION: "1.6"
-  APP_VERSION: "1.6.0"
-  CHART_VERSION: "1.6.0"
+  APP_VERSION: "1.6.1"
+  CHART_VERSION: "1.6.1"
   CACHE_FALLBACK_KEY: "${CI_DEFAULT_BRANCH}"
   # This will supress any download for dependencies and plugins or upload messages which would clutter the console log.
   # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
diff --git a/Makefile b/Makefile
index 11befeed00edf3090a7a0ac04296b95dd3ccbe7d..2479e382e55e3c20f1dd7f9c998fa5807a3f0e05 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 .PHONY: all
 
-APP_VERSION ?= 1.6.0
-CHART_VERSION ?= 1.6.0
+APP_VERSION ?= 1.6.1
+CHART_VERSION ?= 1.6.1
 REPOSITORY_URL ?= registry.datalab.tuwien.ac.at/dbrepo
 
 .PHONY: all
diff --git a/dbrepo-analyse-service/Pipfile b/dbrepo-analyse-service/Pipfile
index 69b9ee840951c7c27bb036d94057477ae73ceb52..831f8e532dd5f5796ca93cc6ca52fa7cecebb608 100644
--- a/dbrepo-analyse-service/Pipfile
+++ b/dbrepo-analyse-service/Pipfile
@@ -21,7 +21,7 @@ numpy = "*"
 pandas = "*"
 minio = "*"
 pydantic = "*"
-dbrepo = {path = "./lib/dbrepo-1.6.0.tar.gz"}
+dbrepo = {path = "./lib/dbrepo-1.6.1.tar.gz"}
 opensearch-py = "*"
 
 [dev-packages]
diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock
index 0a59148511a9af575c5bd78df44601ab2b9d27b8..c668b400e4fa26855881c6b78400b2c66becbc0f 100644
--- a/dbrepo-analyse-service/Pipfile.lock
+++ b/dbrepo-analyse-service/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "e3a8db6afce757927a19df8fe01aea426b0f15868df14c8f98a83f2c19a688da"
+            "sha256": "683cc19a3205b9b5f9b99db8b71c0abadadfd652a94dcf710a73aeca92b97227"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -175,20 +175,20 @@
         },
         "boto3": {
             "hashes": [
-                "sha256:2446e819cf4e295833474cdcf2c92bc82718ce537e9ee1f17f7e3d237f60e69b",
-                "sha256:7de2c44c960e486f3c57e5203ea6393c6c4f0914c5f81c789ceb8b5d2ba5d1c5"
+                "sha256:258ab77225a81d3cf3029c9afe9920cd9dec317689dfadec6f6f0a23130bb60a",
+                "sha256:eb21380d73fec6645439c0d802210f72a0cdb3295b02953f246ff53f512faa8f"
             ],
             "index": "pypi",
             "markers": "python_version >= '3.8'",
-            "version": "==1.35.93"
+            "version": "==1.36.1"
         },
         "botocore": {
             "hashes": [
-                "sha256:47f7161000af6036f806449e3de12acdd3ec11aac7f5578e43e96241413a0f8f",
-                "sha256:b8d245a01e7d64c41edcf75a42be158df57b9518a83a3dbf5c7e4b8c2bc540cc"
+                "sha256:dec513b4eb8a847d79bbefdcdd07040ed9d44c20b0001136f0890a03d595705a",
+                "sha256:f789a6f272b5b3d8f8756495019785e33868e5e00dd9662a3ee7959ac939bb12"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==1.35.93"
+            "version": "==1.36.1"
         },
         "certifi": {
             "hashes": [
@@ -268,7 +268,7 @@
                 "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
                 "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
             ],
-            "markers": "platform_python_implementation != 'PyPy'",
+            "markers": "python_version >= '3.8'",
             "version": "==1.17.1"
         },
         "charset-normalizer": {
@@ -412,9 +412,9 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:769c6d9c4475b26d3f752dcc9910346798d31afeca45ef014f48dd293074b8fb"
+                "sha256:251f3c2088bbd289cee86d5394b1e62e29aa081f994dd0845d895e3330f6a106"
             ],
-            "path": "./lib/dbrepo-1.6.0.tar.gz"
+            "path": "./lib/dbrepo-1.6.1.tar.gz"
         },
         "events": {
             "hashes": [
@@ -1230,12 +1230,12 @@
         },
         "pydantic": {
             "hashes": [
-                "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d",
-                "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"
+                "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff",
+                "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"
             ],
             "index": "pypi",
             "markers": "python_version >= '3.8'",
-            "version": "==2.10.4"
+            "version": "==2.10.5"
         },
         "pydantic-core": {
             "hashes": [
@@ -1427,11 +1427,11 @@
         },
         "referencing": {
             "hashes": [
-                "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c",
-                "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"
+                "sha256:363d9c65f080d0d70bc41c721dce3c7f3e77fc09f269cd5c8813da18069a6794",
+                "sha256:ca2e6492769e3602957e9b831b94211599d2aade9477f5d44110d2530cf9aade"
             ],
-            "markers": "python_version >= '3.8'",
-            "version": "==0.35.1"
+            "markers": "python_version >= '3.9'",
+            "version": "==0.36.1"
         },
         "requests": {
             "hashes": [
@@ -1553,19 +1553,19 @@
         },
         "s3transfer": {
             "hashes": [
-                "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e",
-                "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7"
+                "sha256:3f25c900a367c8b7f7d8f9c34edc87e300bde424f779dc9f0a8ae4f9df9264f6",
+                "sha256:8fa0aa48177be1f3425176dfe1ab85dcd3d962df603c3dbfc585e6bf857ef0ff"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==0.10.4"
+            "version": "==0.11.1"
         },
         "setuptools": {
             "hashes": [
-                "sha256:84fb203f278ebcf5cd08f97d3fb96d3fbed4b629d500b29ad60d11e00769b183",
-                "sha256:886ff7b16cd342f1d1defc16fc98c9ce3fde69e087a4e1983d7ab634e5f41f4f"
+                "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6",
+                "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"
             ],
             "markers": "python_version >= '3.9'",
-            "version": "==75.7.0"
+            "version": "==75.8.0"
         },
         "six": {
             "hashes": [
@@ -1877,7 +1877,7 @@
                 "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
                 "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
             ],
-            "markers": "platform_python_implementation != 'PyPy'",
+            "markers": "python_version >= '3.8'",
             "version": "==1.17.1"
         },
         "charset-normalizer": {
@@ -2241,74 +2241,88 @@
         },
         "wrapt": {
             "hashes": [
-                "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d",
-                "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301",
-                "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635",
-                "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a",
-                "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed",
-                "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721",
-                "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801",
-                "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b",
-                "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1",
-                "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88",
-                "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8",
-                "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0",
-                "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f",
-                "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578",
-                "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7",
-                "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045",
-                "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada",
-                "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d",
-                "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b",
-                "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a",
-                "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977",
-                "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea",
-                "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346",
-                "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13",
-                "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22",
-                "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339",
-                "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9",
-                "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181",
-                "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c",
-                "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90",
-                "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a",
-                "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489",
-                "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f",
-                "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504",
-                "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea",
-                "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569",
-                "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4",
-                "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce",
-                "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab",
-                "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a",
-                "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f",
-                "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c",
-                "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9",
-                "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf",
-                "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d",
-                "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627",
-                "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d",
-                "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4",
-                "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c",
-                "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d",
-                "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad",
-                "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b",
-                "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33",
-                "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371",
-                "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1",
-                "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393",
-                "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106",
-                "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df",
-                "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379",
-                "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451",
-                "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b",
-                "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575",
-                "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed",
-                "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb",
-                "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"
+                "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f",
+                "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c",
+                "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a",
+                "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b",
+                "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555",
+                "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c",
+                "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b",
+                "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6",
+                "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8",
+                "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662",
+                "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061",
+                "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998",
+                "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb",
+                "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62",
+                "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984",
+                "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392",
+                "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2",
+                "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306",
+                "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7",
+                "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3",
+                "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9",
+                "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6",
+                "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192",
+                "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317",
+                "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f",
+                "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda",
+                "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563",
+                "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a",
+                "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f",
+                "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d",
+                "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9",
+                "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8",
+                "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82",
+                "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9",
+                "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845",
+                "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82",
+                "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125",
+                "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504",
+                "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b",
+                "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7",
+                "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc",
+                "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6",
+                "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40",
+                "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a",
+                "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3",
+                "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a",
+                "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72",
+                "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681",
+                "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438",
+                "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae",
+                "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2",
+                "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb",
+                "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5",
+                "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a",
+                "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3",
+                "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8",
+                "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2",
+                "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22",
+                "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72",
+                "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061",
+                "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f",
+                "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9",
+                "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04",
+                "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98",
+                "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9",
+                "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f",
+                "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b",
+                "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925",
+                "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6",
+                "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0",
+                "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9",
+                "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c",
+                "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991",
+                "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6",
+                "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000",
+                "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb",
+                "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119",
+                "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b",
+                "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==1.17.0"
+            "version": "==1.17.2"
         }
     }
 }
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.5.0.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.5.0.tar.gz
deleted file mode 100644
index dfe9572952b2d1408cf85e7eef799ba83e8f60a0..0000000000000000000000000000000000000000
Binary files a/dbrepo-analyse-service/lib/dbrepo-1.5.0.tar.gz and /dev/null differ
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.5.1.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.5.1.tar.gz
deleted file mode 100644
index ca01f15711ae3ba792d5ecfca40356b50dda6b15..0000000000000000000000000000000000000000
Binary files a/dbrepo-analyse-service/lib/dbrepo-1.5.1.tar.gz and /dev/null differ
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.5.2.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.5.2.tar.gz
deleted file mode 100644
index 52bf0fe9a030ce509450bc52427eb7e2c1f1da13..0000000000000000000000000000000000000000
Binary files a/dbrepo-analyse-service/lib/dbrepo-1.5.2.tar.gz and /dev/null differ
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.5.3.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.5.3.tar.gz
deleted file mode 100644
index 2bb796d8fece2d97c3b2168248ff493dfa24a549..0000000000000000000000000000000000000000
Binary files a/dbrepo-analyse-service/lib/dbrepo-1.5.3.tar.gz and /dev/null differ
diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..7914db1bb84dddf85611cda3b766c0c0cdc094c7
Binary files /dev/null and b/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz differ
diff --git a/dbrepo-auth-service/init/app.py b/dbrepo-auth-service/init/app.py
index 7d43f163fa3f50e326ba71933674500121d9fef2..948ed9fe2c16124367d0bb206177128310d45e5e 100644
--- a/dbrepo-auth-service/init/app.py
+++ b/dbrepo-auth-service/init/app.py
@@ -1,4 +1,7 @@
+import logging
 import os
+import sys
+
 import mariadb
 from keycloak import KeycloakAdmin
 
@@ -8,8 +11,24 @@ admin = KeycloakAdmin(server_url=os.getenv('AUTH_SERVICE_ENDPOINT', 'http://loca
                       username=os.getenv('AUTH_SERVICE_ADMIN', 'admin'),
                       password=os.getenv('AUTH_SERVICE_ADMIN_PASSWORD', 'admin'),
                       verify=True)
-user_id = admin.get_user_id(username=system_username)
-print(f'Successfully fetched user id: {user_id}')
+keycloak_user_id = admin.get_user_id(username=system_username)
+logging.info(f'Successfully fetched keycloak user id: {keycloak_user_id}')
+ldap_user = admin.get_user(user_id=keycloak_user_id)
+if ldap_user is None:
+    logging.error(f'Failed to obtain user')
+    sys.exit(1)
+ldap_user_attrs = ldap_user.get('attributes')
+if ldap_user_attrs is None:
+    logging.error(f'Failed to obtain user attributes')
+    sys.exit(1)
+if 'LDAP_ID' not in ldap_user_attrs:
+    logging.error(f'Failed to obtain ldap id: LDAP_ID not in attributes {ldap_user_attrs}')
+    sys.exit(1)
+if len(ldap_user_attrs['LDAP_ID']) != 1:
+    logging.error(f'Failed to obtain ldap id: wrong length {len(ldap_user_attrs["LDAP_ID"])} != 1')
+    sys.exit(1)
+ldap_user_id = ldap_user_attrs['LDAP_ID'][0]
+logging.info(f'Successfully fetched ldap user id: {ldap_user_id}')
 
 try:
     conn = mariadb.connect(user=os.getenv('METADATA_USERNAME', 'root'),
@@ -19,13 +38,13 @@ try:
                            database=os.getenv('METADATA_DB', 'dbrepo'))
     cursor = conn.cursor()
     cursor.execute(
-        "INSERT IGNORE INTO `mdb_users` (`id`, `username`, `email`, `mariadb_password`) VALUES (?, ?, ?, PASSWORD(?))",
-        (user_id, system_username, 'some@admin', '1234567890'))
+        "INSERT IGNORE INTO `mdb_users` (`id`, `username`, `email`, `mariadb_password`, `is_internal`) VALUES (?, ?, LEFT(UUID(), 20), PASSWORD(LEFT(UUID(), 20)), true)",
+        (ldap_user_id, system_username))
     conn.commit()
     conn.close()
 except mariadb.Error as e:
-    print(f"Error connecting to MariaDB Platform: {e}")
+    logging.info(f"Error connecting to MariaDB Platform: {e}")
     exit(1)
 
-print(f'Successfully inserted user')
+logging.info(f'Successfully inserted user')
 exit(0)
diff --git a/dbrepo-data-service/pom.xml b/dbrepo-data-service/pom.xml
index 0b244a12a46ec7d9ec25ba86fd01c9f2d5b97d5f..884824994aff8a6e8f05bce24c44307ffe64c2b8 100644
--- a/dbrepo-data-service/pom.xml
+++ b/dbrepo-data-service/pom.xml
@@ -11,7 +11,7 @@
     <groupId>at.tuwien</groupId>
     <artifactId>dbrepo-data-service</artifactId>
     <name>dbrepo-data-service</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <description>Service that manages the data</description>
 
@@ -60,6 +60,8 @@
         <!-- see https://github.com/apache/spark/blob/cde8e4a82e20a363861f451ebd5138efb3194ab8/pom.xml -->
         <hadoop.version>3.4.0</hadoop.version>
         <jakarta-servlet.version>5.0.0</jakarta-servlet.version>
+        <sonar.coverage.jacoco.xmlReportPaths>./report/target/site/jacoco-aggregate/jacoco.xml
+        </sonar.coverage.jacoco.xmlReportPaths>
     </properties>
 
     <dependencies>
diff --git a/dbrepo-data-service/querystore/pom.xml b/dbrepo-data-service/querystore/pom.xml
index 3917fc4976354fffd39805995f82e9a322742631..943c115d117529adea091f02f77037367c12a32c 100644
--- a/dbrepo-data-service/querystore/pom.xml
+++ b/dbrepo-data-service/querystore/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-data-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-data-service-querystore</artifactId>
     <name>dbrepo-data-service-querystore</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies/>
 
diff --git a/dbrepo-data-service/report/pom.xml b/dbrepo-data-service/report/pom.xml
index 99c9261cea42c59e2daa5c68140e971c410874be..ca03190a449b76f7217590283097978be9d8a0ab 100644
--- a/dbrepo-data-service/report/pom.xml
+++ b/dbrepo-data-service/report/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-data-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>report</artifactId>
     <name>dbrepo-data-service-report</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
     <description>
         This module is only intended for the pipeline coverage report. See the detailed report in the
         respective modules
diff --git a/dbrepo-data-service/rest-service/pom.xml b/dbrepo-data-service/rest-service/pom.xml
index 9eb0cddee6c2577ea82dbe09f71f13192bc1a4c4..e72392c7071cb3083c894fbc3654266f34650f01 100644
--- a/dbrepo-data-service/rest-service/pom.xml
+++ b/dbrepo-data-service/rest-service/pom.xml
@@ -6,18 +6,18 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-data-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>rest-service</artifactId>
     <name>dbrepo-data-service-rest-service</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
             <groupId>at.tuwien</groupId>
             <artifactId>services</artifactId>
-            <version>1.6.0</version>
+            <version>1.6.1</version>
         </dependency>
     </dependencies>
 
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java
index 334128637ec5113e6478567d30e44b2f61ea3f5b..87a4d32532c586c0f2517862f0cd3cc104f4f054 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java
@@ -1,14 +1,50 @@
 package at.tuwien.endpoints;
 
+import at.tuwien.api.user.UserDetailsDto;
 import org.apache.spark.sql.Dataset;
 import org.apache.spark.sql.Row;
+import org.springframework.security.core.Authentication;
 
+import java.security.Principal;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 public abstract class AbstractEndpoint {
 
+    public boolean hasRole(Principal principal, String role) {
+        if (principal == null || role == null) {
+            return false;
+        }
+        final Authentication authentication = (Authentication) principal;
+        return authentication.getAuthorities()
+                .stream()
+                .anyMatch(a -> a.getAuthority().equals(role));
+    }
+
+    public boolean isSystem(Principal principal) {
+        if (principal == null) {
+            return false;
+        }
+        final Authentication authentication = (Authentication) principal;
+        return authentication.getAuthorities()
+                .stream()
+                .anyMatch(a -> a.getAuthority().equals("system"));
+    }
+
+    public UUID getId(Principal principal) {
+        if (principal == null) {
+            return null;
+        }
+        final Authentication authentication = (Authentication) principal;
+        final UserDetailsDto user = (UserDetailsDto) authentication.getPrincipal();
+        if (user.getId() == null) {
+            return null;
+        }
+        return UUID.fromString(user.getId());
+    }
+
     public List<Map<String, Object>> transform(Dataset<Row> dataset) {
         return dataset.collectAsList()
                 .stream()
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
index 64d69a9013647c3bd886a690172994fcc382de95..7947e87a495595441c945e950453499c635aaac2 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
@@ -29,7 +29,7 @@ import java.util.UUID;
 @RestController
 @CrossOrigin(origins = "*")
 @RequestMapping(path = "/api/database/{databaseId}/access")
-public class AccessEndpoint {
+public class AccessEndpoint extends AbstractEndpoint {
 
     private final AccessService accessService;
     private final CredentialService credentialService;
@@ -139,7 +139,7 @@ public class AccessEndpoint {
                 access.getType());
         final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId);
         final PrivilegedUserDto user = credentialService.getUser(userId);
-        if (database.getAccesses().stream().noneMatch(a -> a.getUser().getId().equals(userId))) {
+        if (database.getAccesses().stream().noneMatch(a -> a.getHuserid().equals(userId))) {
             log.error("Failed to update access to user with id {}: no access", userId);
             throw new NotAllowedException("Failed to update access to user with id " + userId + ": no access");
         }
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
index 0043b64dfe742fa97ea8a9bdbef671b5cc6118c1..d101a8c97393a41ecf4e76ea4e4fb4aa3c1f4993 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
@@ -35,7 +35,7 @@ import java.sql.SQLException;
 @RestController
 @CrossOrigin(origins = "*")
 @RequestMapping(path = "/api/database")
-public class DatabaseEndpoint {
+public class DatabaseEndpoint extends AbstractEndpoint {
 
     private final SubsetService queryService;
     private final AccessService accessService;
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java
index 4f1b5d59c94cee09b4666a8171367cd9d737ea27..f30251a5ff09e895a7324a9857134c789f6b3c03 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java
@@ -11,7 +11,6 @@ import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.exception.*;
 import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.*;
-import at.tuwien.utils.UserUtil;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
@@ -73,7 +72,7 @@ public class SubsetEndpoint extends AbstractEndpoint {
     @GetMapping
     @Observed(name = "dbrepo_subset_list")
     @Operation(summary = "Find subsets",
-            description = "Finds subsets in the query store. The result can be optionally filtered by setting `persisted`. When set to *true*, only persisted queries are returned, otherwise only non-persisted queries are returned.",
+            description = "Finds subsets in the query store. When the database schema is marked as hidden, the user needs to be authorized, have at least read-access to the database. The result can be optionally filtered by setting `persisted`. When set to *true*, only persisted queries are returned, otherwise only non-persisted queries are returned.",
             security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200",
@@ -98,11 +97,13 @@ public class SubsetEndpoint extends AbstractEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<List<QueryDto>> list(@NotNull @PathVariable("databaseId") Long databaseId,
-                                               @RequestParam(name = "persisted", required = false) Boolean filterPersisted)
+                                               @RequestParam(name = "persisted", required = false) Boolean filterPersisted,
+                                               Principal principal)
             throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException,
             QueryNotFoundException, NotAllowedException, MetadataServiceException {
         log.debug("endpoint find subsets in database, databaseId={}, filterPersisted={}", databaseId, filterPersisted);
         final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId);
+        endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
         final List<QueryDto> queries;
         try {
             queries = subsetService.findAll(database, filterPersisted);
@@ -117,7 +118,7 @@ public class SubsetEndpoint extends AbstractEndpoint {
     @GetMapping("/{subsetId}")
     @Observed(name = "dbrepo_subset_find")
     @Operation(summary = "Find subset",
-            description = "Finds a subset in the data database. Requests with HTTP header `Accept=application/json` return the metadata, requests with HTTP header `Accept=text/csv` return the data as downloadable file.",
+            description = "Finds a subset in the data database.  When the database schema is marked as hidden, the user needs to be authorized, have at least read-access to the database.  Requests with HTTP header `Accept=application/json` return the metadata, requests with HTTP header `Accept=text/csv` return the data as downloadable file.",
             security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200",
@@ -154,15 +155,17 @@ public class SubsetEndpoint extends AbstractEndpoint {
     })
     public ResponseEntity<?> findById(@NotNull @PathVariable("databaseId") Long databaseId,
                                       @NotNull @PathVariable("subsetId") Long subsetId,
-                                      @NotNull HttpServletRequest httpServletRequest,
-                                      @RequestParam(required = false) Instant timestamp)
+                                      @NotNull @RequestHeader("Accept") String accept,
+                                      @RequestParam(required = false) Instant timestamp,
+                                      Principal principal)
             throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException,
             QueryNotFoundException, FormatNotAvailableException, StorageUnavailableException, UserNotFoundException,
-            MetadataServiceException, TableNotFoundException, ViewMalformedException, QueryMalformedException {
-        String accept = httpServletRequest.getHeader("Accept");
+            MetadataServiceException, TableNotFoundException, ViewMalformedException, QueryMalformedException,
+            NotAllowedException {
         log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId,
                 subsetId, accept, timestamp);
         final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId);
+        endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
         final QueryDto subset;
         try {
             subset = subsetService.findById(database, subsetId);
@@ -175,7 +178,7 @@ public class SubsetEndpoint extends AbstractEndpoint {
             timestamp = Instant.now();
             log.debug("timestamp not set: default to {}", timestamp);
         }
-        if (accept == null) {
+        if (accept == null || accept.isEmpty() || accept.isBlank()) {
             accept = MediaType.APPLICATION_JSON_VALUE;
             log.debug("accept header not set: default to {}", accept);
         }
@@ -200,13 +203,13 @@ public class SubsetEndpoint extends AbstractEndpoint {
                     throw new DatabaseUnavailableException("Failed to find data: " + e.getMessage(), e);
                 }
         }
-        throw new FormatNotAvailableException("Must provide either application/json or text/csv headers");
+        throw new FormatNotAvailableException("Must provide either application/json or text/csv value for header 'Accept': provided " + accept + " instead");
     }
 
     @PostMapping
     @Observed(name = "dbrepo_subset_create")
     @Operation(summary = "Create subset",
-            description = "Creates a subset in the query store of the data database. Requires role `execute-query` for private databases.",
+            description = "Creates a subset in the query store of the data database. Can also be used without authentication if (and only if) the database is marked as public (i.e. when `is_public` = `is_schema_public` is set to `true`). Otherwise at least read access is required.",
             security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "201",
@@ -264,7 +267,12 @@ public class SubsetEndpoint extends AbstractEndpoint {
         endpointValidator.validateDataParams(page, size);
         endpointValidator.validateForbiddenStatements(data.getStatement());
         /* parameters */
-        final UUID userId = principal != null ? UserUtil.getId(principal) : null;
+        final UUID userId;
+        if (principal != null) {
+            userId = getId(principal);
+        } else {
+            userId = null;
+        }
         if (page == null) {
             page = 0L;
             log.debug("page not set: default to {}", page);
@@ -279,6 +287,7 @@ public class SubsetEndpoint extends AbstractEndpoint {
         }
         /* create */
         final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId);
+        endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
         try {
             final Long subsetId = subsetService.create(database, data.getStatement(), timestamp, userId);
             return getData(databaseId, subsetId, principal, request, page, size);
@@ -342,8 +351,9 @@ public class SubsetEndpoint extends AbstractEndpoint {
                 log.error("Failed to re-execute query: no authentication found");
                 throw new NotAllowedException("Failed to re-execute query: no authentication found");
             }
-            credentialService.getAccess(databaseId, UserUtil.getId(principal));
+            credentialService.getAccess(databaseId, getId(principal));
         }
+        log.trace("visibility for database: is_public={}, is_schema_public={}", database.getIsPublic(), database.getIsSchemaPublic());
         /* parameters */
         if (page == null) {
             page = 0L;
@@ -426,7 +436,7 @@ public class SubsetEndpoint extends AbstractEndpoint {
         log.debug("endpoint persist query, databaseId={}, queryId={}, data.persist={}, principal.name={}", databaseId,
                 queryId, data.getPersist(), principal.getName());
         final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId);
-        credentialService.getAccess(databaseId, UserUtil.getId(principal));
+        credentialService.getAccess(databaseId, getId(principal));
         try {
             subsetService.persist(database, queryId, data.getPersist());
             final QueryDto dto = subsetService.findById(database, queryId);
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index 11720b49063e9ba0c1dbc1b1f844aa215a6b3b4d..f0ec00a035e729157726b65af9f3c85949581c4e 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -13,7 +13,6 @@ import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.exception.*;
 import at.tuwien.gateway.MetadataServiceGateway;
 import at.tuwien.service.*;
-import at.tuwien.utils.UserUtil;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
@@ -277,7 +276,7 @@ public class TableEndpoint extends AbstractEndpoint {
                 log.error("Failed find table data: authentication required");
                 throw new NotAllowedException("Failed to find table data: authentication required");
             }
-            credentialService.getAccess(databaseId, UserUtil.getId(principal));
+            credentialService.getAccess(databaseId, getId(principal));
         }
         try {
             final HttpHeaders headers = new HttpHeaders();
@@ -335,17 +334,18 @@ public class TableEndpoint extends AbstractEndpoint {
     public ResponseEntity<Void> insertRawTuple(@NotNull @PathVariable("databaseId") Long databaseId,
                                                @NotNull @PathVariable("tableId") Long tableId,
                                                @Valid @RequestBody TupleDto data,
-                                               @NotNull Principal principal)
+                                               @NotNull Principal principal,
+                                               @RequestHeader("Authorization") String authorization)
             throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException,
             TableMalformedException, QueryMalformedException, NotAllowedException, StorageUnavailableException,
             StorageNotFoundException, MetadataServiceException {
         log.debug("endpoint insert raw table data, databaseId={}, tableId={}", databaseId, tableId);
         final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId);
-        final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal));
-        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal));
+        final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal));
+        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal));
         try {
             tableService.createTuple(table, data);
-            metadataServiceGateway.updateTableStatistics(databaseId, tableId);
+            metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization);
             return ResponseEntity.status(HttpStatus.CREATED)
                     .build();
         } catch (SQLException e) {
@@ -387,17 +387,18 @@ public class TableEndpoint extends AbstractEndpoint {
     public ResponseEntity<Void> updateRawTuple(@NotNull @PathVariable("databaseId") Long databaseId,
                                                @NotNull @PathVariable("tableId") Long tableId,
                                                @Valid @RequestBody TupleUpdateDto data,
-                                               @NotNull Principal principal)
+                                               @NotNull Principal principal,
+                                               @RequestHeader("Authorization") String authorization)
             throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException,
             TableMalformedException, QueryMalformedException, NotAllowedException, MetadataServiceException {
         log.debug("endpoint update raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId,
                 data.getKeys());
         final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId);
-        final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal));
-        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal));
+        final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal));
+        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal));
         try {
             tableService.updateTuple(table, data);
-            metadataServiceGateway.updateTableStatistics(databaseId, tableId);
+            metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization);
             return ResponseEntity.status(HttpStatus.ACCEPTED)
                     .build();
         } catch (SQLException e) {
@@ -439,17 +440,18 @@ public class TableEndpoint extends AbstractEndpoint {
     public ResponseEntity<Void> deleteRawTuple(@NotNull @PathVariable("databaseId") Long databaseId,
                                                @NotNull @PathVariable("tableId") Long tableId,
                                                @Valid @RequestBody TupleDeleteDto data,
-                                               @NotNull Principal principal)
+                                               @NotNull Principal principal,
+                                               @RequestHeader("Authorization") String authorization)
             throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException,
             TableMalformedException, QueryMalformedException, NotAllowedException, MetadataServiceException {
         log.debug("endpoint delete raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId,
                 data.getKeys());
         final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId);
-        final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal));
-        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal));
+        final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal));
+        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal));
         try {
             tableService.deleteTuple(table, data);
-            metadataServiceGateway.updateTableStatistics(databaseId, tableId);
+            metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization);
             return ResponseEntity.status(HttpStatus.ACCEPTED)
                     .build();
         } catch (SQLException e) {
@@ -510,7 +512,7 @@ public class TableEndpoint extends AbstractEndpoint {
                 log.error("Failed to find table history: no authentication found");
                 throw new NotAllowedException("Failed to find table history: no authentication found");
             }
-            credentialService.getAccess(databaseId, UserUtil.getId(principal));
+            credentialService.getAccess(databaseId, getId(principal));
         }
         try {
             final List<TableHistoryDto> dto = tableService.history(table, size);
@@ -622,7 +624,7 @@ public class TableEndpoint extends AbstractEndpoint {
                 log.error("Failed to export private table: principal is null");
                 throw new NotAllowedException("Failed to export private table: principal is null");
             }
-            credentialService.getAccess(databaseId, UserUtil.getId(principal));
+            credentialService.getAccess(databaseId, getId(principal));
         }
         final Dataset<Row> dataset = tableService.getData(table.getDatabase(), table.getInternalName(), timestamp, null,
                 null, null, null);
@@ -669,14 +671,15 @@ public class TableEndpoint extends AbstractEndpoint {
     public ResponseEntity<Void> importDataset(@NotNull @PathVariable("databaseId") Long databaseId,
                                               @NotNull @PathVariable("tableId") Long tableId,
                                               @Valid @RequestBody ImportDto data,
-                                              @NotNull Principal principal)
+                                              @NotNull Principal principal,
+                                              @RequestHeader("Authorization") String authorization)
             throws RemoteUnavailableException, TableNotFoundException, NotAllowedException, MetadataServiceException,
             StorageNotFoundException, MalformedException, StorageUnavailableException, QueryMalformedException,
             DatabaseUnavailableException {
         log.debug("endpoint insert table data, databaseId={}, tableId={}, data.location={}", databaseId, tableId, data.getLocation());
         final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId);
-        final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal));
-        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal));
+        final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal));
+        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal));
         if (data.getLineTermination() == null) {
             data.setLineTermination("\\r\\n");
             log.debug("line termination not present, default to {}", data.getLineTermination());
@@ -687,7 +690,7 @@ public class TableEndpoint extends AbstractEndpoint {
             log.error("Failed to establish connection to database: {}", e.getMessage());
             throw new DatabaseUnavailableException("Failed to establish connection to database", e);
         }
-        metadataServiceGateway.updateTableStatistics(databaseId, tableId);
+        metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization);
         return ResponseEntity.accepted()
                 .build();
     }
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
index c8da4239861264539b2140c64fde2b95a8fb8bc4..2b0463483d66429dfb0afd942bbc80ccdfb2fd26 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
@@ -9,7 +9,6 @@ import at.tuwien.api.database.internal.PrivilegedViewDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.exception.*;
 import at.tuwien.service.*;
-import at.tuwien.utils.UserUtil;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
@@ -275,7 +274,7 @@ public class ViewEndpoint extends AbstractEndpoint {
                 log.error("Failed to get data from view: unauthorized");
                 throw new NotAllowedException("Failed to get data from view: unauthorized");
             }
-            credentialService.getAccess(databaseId, UserUtil.getId(principal));
+            credentialService.getAccess(databaseId, getId(principal));
         }
         try {
             final HttpHeaders headers = new HttpHeaders();
@@ -351,7 +350,7 @@ public class ViewEndpoint extends AbstractEndpoint {
                 log.error("Failed to export private view: principal is null");
                 throw new NotAllowedException("Failed to export private view: principal is null");
             }
-            credentialService.getAccess(databaseId, UserUtil.getId(principal));
+            credentialService.getAccess(databaseId, getId(principal));
         }
         final Dataset<Row> dataset = tableService.getData(view.getDatabase(), view.getInternalName(), timestamp, null,
                 null, null, null);
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/utils/UserUtil.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/utils/UserUtil.java
deleted file mode 100644
index 7a99e839edd3b97758e713260f798ae5357c53c6..0000000000000000000000000000000000000000
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/utils/UserUtil.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package at.tuwien.utils;
-
-import at.tuwien.api.user.UserDetailsDto;
-import org.springframework.security.core.Authentication;
-
-import java.security.Principal;
-import java.util.UUID;
-
-public class UserUtil {
-
-    public static boolean hasRole(Principal principal, String role) {
-        if (principal == null || role == null) {
-            return false;
-        }
-        final Authentication authentication = (Authentication) principal;
-        return authentication.getAuthorities()
-                .stream()
-                .anyMatch(a -> a.getAuthority().equals(role));
-    }
-
-    public static UUID getId(Principal principal) {
-        if (principal == null) {
-            return null;
-        }
-        final Authentication authentication = (Authentication) principal;
-        final UserDetailsDto user = (UserDetailsDto) authentication.getPrincipal();
-        if (user.getId() == null) {
-            return null;
-        }
-        return UUID.fromString(user.getId());
-    }
-
-}
diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
index 1c6adfd6a5b87355e76f9b40147fe91f5de4d4e2..25b858f51bd3f643f7879b9eac2dbb648b00aabb 100644
--- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
+++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
@@ -1,14 +1,17 @@
 package at.tuwien.validation;
 
 import at.tuwien.api.database.AccessTypeDto;
+import at.tuwien.api.database.DatabaseAccessDto;
+import at.tuwien.api.database.internal.PrivilegedDatabaseDto;
 import at.tuwien.config.QueryConfig;
-import at.tuwien.exception.NotAllowedException;
-import at.tuwien.exception.PaginationException;
-import at.tuwien.exception.QueryNotSupportedException;
+import at.tuwien.endpoints.AbstractEndpoint;
+import at.tuwien.exception.*;
+import at.tuwien.gateway.MetadataServiceGateway;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.security.Principal;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
@@ -18,13 +21,15 @@ import java.util.regex.Pattern;
 
 @Log4j2
 @Component
-public class EndpointValidator {
+public class EndpointValidator extends AbstractEndpoint {
 
     private final QueryConfig queryConfig;
+    private final MetadataServiceGateway metadataServiceGateway;
 
     @Autowired
-    public EndpointValidator(QueryConfig queryConfig) {
+    public EndpointValidator(QueryConfig queryConfig, MetadataServiceGateway metadataServiceGateway) {
         this.queryConfig = queryConfig;
+        this.metadataServiceGateway = metadataServiceGateway;
     }
 
     public void validateDataParams(Long page, Long size) throws PaginationException {
@@ -43,6 +48,56 @@ public class EndpointValidator {
         }
     }
 
+    public void validateOnlyPrivateSchemaAccess(PrivilegedDatabaseDto database, Principal principal)
+            throws NotAllowedException, RemoteUnavailableException, MetadataServiceException {
+        validateOnlyPrivateSchemaAccess(database, principal, false);
+    }
+
+    public void validateOnlyPrivateSchemaAccess(PrivilegedDatabaseDto database, Principal principal,
+                                                boolean writeAccessOnly) throws NotAllowedException,
+            RemoteUnavailableException, MetadataServiceException {
+        if (database.getIsSchemaPublic()) {
+            log.trace("database schema with id {} is public: no access needed", database.getId());
+            return;
+        }
+        validateOnlyAccess(database, principal, writeAccessOnly);
+    }
+
+    public void validateOnlyPrivateSchemaHasRole(PrivilegedDatabaseDto database, Principal principal, String role)
+            throws NotAllowedException {
+        if (database.getIsSchemaPublic()) {
+            log.trace("database with id {} has public schema: no access needed", database.getId());
+            return;
+        }
+        log.trace("database with id {} has private schema", database.getId());
+        if (principal == null) {
+            log.error("Access not allowed: no authorization provided");
+            throw new NotAllowedException("Access not allowed: no authorization provided");
+        }
+        log.trace("principal: {}", principal.getName());
+        if (!hasRole(principal, role)) {
+            log.error("Access not allowed: role {} missing", role);
+            throw new NotAllowedException("Access not allowed: role " + role + " missing");
+        }
+        log.trace("principal has role '{}': access granted", role);
+    }
+
+    public void validateOnlyAccess(PrivilegedDatabaseDto database, Principal principal, boolean writeAccessOnly)
+            throws NotAllowedException, RemoteUnavailableException, MetadataServiceException {
+        if (principal == null) {
+            throw new NotAllowedException("No principal provided");
+        }
+        if (isSystem(principal)) {
+            return;
+        }
+        final DatabaseAccessDto access = metadataServiceGateway.getAccess(database.getId(), getId(principal));
+        log.trace("found access: {}", access);
+        if (writeAccessOnly && !(access.getType().equals(AccessTypeDto.WRITE_OWN) || access.getType().equals(AccessTypeDto.WRITE_ALL))) {
+            log.error("Access not allowed: no write access");
+            throw new NotAllowedException("Access not allowed: no write access");
+        }
+    }
+
     public void validateForbiddenStatements(String query) throws QueryNotSupportedException {
         final List<String> words = new LinkedList<>();
         Arrays.stream(queryConfig.getForbiddenKeywords())
diff --git a/dbrepo-data-service/rest-service/src/main/resources/application.yml b/dbrepo-data-service/rest-service/src/main/resources/application.yml
index 03d89895cb607cee20bc84cd8cd3195c029a9ab8..b5f592d570cb73677baaa00666af945901e1d256 100644
--- a/dbrepo-data-service/rest-service/src/main/resources/application.yml
+++ b/dbrepo-data-service/rest-service/src/main/resources/application.yml
@@ -74,7 +74,7 @@ dbrepo:
     default:
       read: "${GRANT_DEFAULT_READ:SELECT}"
       write: "${GRANT_DEFAULT_WRITE:SELECT, CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TEMPORARY TABLES, LOCK TABLES, INDEX, TRIGGER, INSERT, UPDATE, DELETE}"
-  credentialCacheTimeout: "${CREDENTIAL_CACHE_TIMEOUT:3600}"
+  credentialCacheTimeout: "${CREDENTIAL_CACHE_TIMEOUT:60}"
   minConcurrent: "${MIN_CONCURRENT_CONSUMERS:2}"
   maxConcurrent: "${MAX_CONCURRENT_CONSUMERS:6}"
   requeueRejected: ${REQUEUE_REJECTED:false}
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
index e7de4a04b3b48d089d254d4da2d442ba4a5334f7..8a08f8231fe0de7babf1db995dc60786407425f8 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java
@@ -6,6 +6,7 @@ import at.tuwien.api.database.query.QueryDto;
 import at.tuwien.api.database.query.QueryPersistDto;
 import at.tuwien.endpoints.SubsetEndpoint;
 import at.tuwien.exception.*;
+import at.tuwien.gateway.MetadataServiceGateway;
 import at.tuwien.service.CredentialService;
 import at.tuwien.service.SchemaService;
 import at.tuwien.service.StorageService;
@@ -23,13 +24,13 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.security.test.context.support.WithAnonymousUser;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
+import java.security.Principal;
 import java.sql.SQLException;
 import java.time.Instant;
 import java.util.List;
@@ -55,6 +56,9 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
     @MockBean
     private SchemaService schemaService;
 
+    @MockBean
+    private MetadataServiceGateway metadataServiceGateway;
+
     @MockBean
     private HttpServletRequest httpServletRequest;
 
@@ -74,15 +78,31 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void list_succeeds() throws DatabaseUnavailableException, NotAllowedException, QueryNotFoundException,
-            DatabaseNotFoundException, RemoteUnavailableException, SQLException, MetadataServiceException {
+    public void list_publicDataPrivateSchemaAnonymous_fails() throws QueryNotFoundException, DatabaseNotFoundException,
+            RemoteUnavailableException, SQLException, MetadataServiceException {
 
         /* mock */
         when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null))
                 .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO));
 
         /* test */
-        final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO);
+        assertThrows(NotAllowedException.class, () -> {
+            generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME)
+    public void list_publicDataPrivateSchema_succeeds() throws DatabaseUnavailableException, NotAllowedException,
+            QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, SQLException,
+            MetadataServiceException {
+
+        /* mock */
+        when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null))
+                .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO));
+
+        /* test */
+        final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_3_PRINCIPAL);
         assertEquals(6, response.size());
     }
 
@@ -92,14 +112,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseNotFoundException.class, () -> {
-            generic_list(null, null);
+            generic_list(null, null, null);
         });
     }
 
     @Test
-    @WithAnonymousUser
-    public void list_unavailable_fails() throws SQLException, QueryNotFoundException, DatabaseNotFoundException,
-            RemoteUnavailableException, MetadataServiceException {
+    @WithMockUser(username = USER_3_USERNAME)
+    public void list_publicDataAndPrivateSchemaUnavailable_fails() throws SQLException, QueryNotFoundException,
+            DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException {
 
         /* mock */
         when(credentialService.getDatabase(DATABASE_3_ID))
@@ -110,15 +130,48 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO);
+            generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_3_PRINCIPAL);
         });
     }
 
     @Test
     @WithAnonymousUser
-    public void findById_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException,
-            DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, QueryNotFoundException,
-            FormatNotAvailableException, TableNotFoundException, MetadataServiceException, SQLException,
+    public void findById_privateDataPrivateSchemaAnonymous_fails() throws DatabaseNotFoundException, SQLException,
+            RemoteUnavailableException, UserNotFoundException, QueryNotFoundException, MetadataServiceException {
+
+        /* mock */
+        when(credentialService.getDatabase(DATABASE_1_ID))
+                .thenReturn(DATABASE_1_PRIVILEGED_DTO);
+        when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID))
+                .thenReturn(QUERY_1_DTO);
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_findById(DATABASE_1_ID, QUERY_1_ID, "application/json", null, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void findById_privateDataPrivateSchema_succeeds() throws DatabaseNotFoundException, SQLException,
+            RemoteUnavailableException, UserNotFoundException, QueryNotFoundException, MetadataServiceException, DatabaseUnavailableException, TableNotFoundException, StorageUnavailableException, NotAllowedException, ViewMalformedException, QueryMalformedException, FormatNotAvailableException {
+
+        /* mock */
+        when(credentialService.getDatabase(DATABASE_1_ID))
+                .thenReturn(DATABASE_1_PRIVILEGED_DTO);
+        when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID))
+                .thenReturn(QUERY_1_DTO);
+
+        /* test */
+        generic_findById(DATABASE_1_ID, QUERY_1_ID, "application/json", null, USER_1_PRINCIPAL);
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME)
+    public void findById_publicDataPrivateSchema_succeeds() throws DatabaseNotFoundException, SQLException,
+            RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException,
+            StorageUnavailableException, QueryMalformedException, QueryNotFoundException,
+            FormatNotAvailableException, TableNotFoundException, MetadataServiceException,
             ViewMalformedException {
 
         /* mock */
@@ -128,7 +181,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(QUERY_5_DTO);
 
         /* test */
-        generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null);
+        generic_findById(DATABASE_3_ID, QUERY_5_ID, "application/json", null, USER_3_PRINCIPAL);
     }
 
     @Test
@@ -137,29 +190,29 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
             UserNotFoundException, QueryNotFoundException, MetadataServiceException {
 
         /* mock */
-        when(credentialService.getDatabase(DATABASE_3_ID))
-                .thenReturn(DATABASE_3_PRIVILEGED_DTO);
-        when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID))
-                .thenReturn(QUERY_5_DTO);
+        when(credentialService.getDatabase(DATABASE_4_ID))
+                .thenReturn(DATABASE_4_PRIVILEGED_DTO);
+        when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_7_ID))
+                .thenReturn(QUERY_7_DTO);
 
         /* test */
         assertThrows(FormatNotAvailableException.class, () -> {
-            generic_findById(QUERY_5_ID, MediaType.APPLICATION_PDF, null);
+            generic_findById(DATABASE_4_ID, QUERY_7_ID, "application/pdf", null, null);
         });
     }
 
     @Test
-    @WithAnonymousUser
-    public void findById_acceptCsv_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
+    @WithMockUser(username = USER_1_USERNAME)
+    public void findById_privateDataPrivateSchemaAcceptCsv_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
             UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException,
             QueryNotFoundException, FormatNotAvailableException, SQLException, MetadataServiceException,
-            TableNotFoundException, ViewMalformedException {
+            TableNotFoundException, ViewMalformedException, NotAllowedException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
-        when(credentialService.getDatabase(DATABASE_3_ID))
-                .thenReturn(DATABASE_3_PRIVILEGED_DTO);
-        when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID))
+        when(credentialService.getDatabase(DATABASE_1_ID))
+                .thenReturn(DATABASE_1_PRIVILEGED_DTO);
+        when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID))
                 .thenReturn(QUERY_5_DTO);
         when(storageService.transformDataset(any(Dataset.class)))
                 .thenReturn(EXPORT_RESOURCE_DTO);
@@ -167,15 +220,15 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(mock);
 
         /* test */
-        generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), null);
+        generic_findById(DATABASE_1_ID, QUERY_1_ID, "text/csv", null, USER_1_PRINCIPAL);
     }
 
     @Test
     @WithAnonymousUser
-    public void findById_timestamp_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
-            UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException,
-            QueryNotFoundException, FormatNotAvailableException, SQLException, MetadataServiceException,
-            TableNotFoundException, ViewMalformedException {
+    public void findById_publicDataPrivateSchemaAnonymous_fails() throws DatabaseNotFoundException,
+            RemoteUnavailableException, UserNotFoundException, StorageUnavailableException, QueryMalformedException,
+            QueryNotFoundException, SQLException, MetadataServiceException, TableNotFoundException,
+            ViewMalformedException {
         final Dataset<Row> mock = sparkSession.emptyDataFrame();
 
         /* mock */
@@ -189,12 +242,38 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(EXPORT_RESOURCE_DTO);
 
         /* test */
-        generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), Instant.now());
+        assertThrows(NotAllowedException.class, () -> {
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, "text/csv", Instant.now(), null);
+        });
     }
 
     @Test
     @WithAnonymousUser
-    public void findById_notFound_fails() throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException {
+    public void findById_publicDataPublicSchemaAnonymous_fails() throws DatabaseNotFoundException, SQLException,
+            RemoteUnavailableException, UserNotFoundException, QueryMalformedException, StorageUnavailableException,
+            QueryNotFoundException, MetadataServiceException, TableNotFoundException, ViewMalformedException {
+        final Dataset<Row> mock = sparkSession.emptyDataFrame();
+
+        /* mock */
+        when(credentialService.getDatabase(DATABASE_3_ID))
+                .thenReturn(DATABASE_3_PRIVILEGED_DTO);
+        when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_5_ID))
+                .thenReturn(QUERY_5_DTO);
+        when(subsetService.getData(any(PrivilegedDatabaseDto.class), any(QueryDto.class), eq(null), eq(null)))
+                .thenReturn(mock);
+        when(storageService.transformDataset(any(Dataset.class)))
+                .thenReturn(EXPORT_RESOURCE_DTO);
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, "text/csv", Instant.now(), null);
+        });
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findById_notFound_fails() throws DatabaseNotFoundException, RemoteUnavailableException,
+            MetadataServiceException {
 
         /* mock */
         doThrow(DatabaseNotFoundException.class)
@@ -203,13 +282,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseNotFoundException.class, () -> {
-            generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null);
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, "application/json", null, null);
         });
     }
 
     @Test
-    @WithAnonymousUser
-    public void findById_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException,
+    @WithMockUser(username = USER_3_USERNAME)
+    public void findById_publicDataAndPrivateSchemaUnavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException,
             MetadataServiceException, SQLException, UserNotFoundException, QueryNotFoundException {
 
         /* mock */
@@ -221,13 +300,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null);
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, "application/json", null, USER_3_PRINCIPAL);
         });
     }
 
     @Test
-    @WithAnonymousUser
-    public void findById_unavailableExport_fails() throws DatabaseNotFoundException, RemoteUnavailableException,
+    @WithMockUser(username = USER_1_USERNAME)
+    public void findById_publicDataPrivateSchemaUnavailableExport_fails() throws DatabaseNotFoundException, RemoteUnavailableException,
             MetadataServiceException, SQLException, QueryMalformedException, UserNotFoundException,
             QueryNotFoundException, TableNotFoundException, ViewMalformedException, StorageUnavailableException {
 
@@ -244,7 +323,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), null);
+            generic_findById(DATABASE_3_ID, QUERY_5_ID, "text/csv", null, USER_1_PRINCIPAL);
         });
     }
 
@@ -326,7 +405,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"})
     public void create_unavailable_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
             SQLException, MetadataServiceException, QueryMalformedException, TableNotFoundException,
-            ViewMalformedException, UserNotFoundException, QueryNotFoundException, QueryStoreInsertException {
+            ViewMalformedException, UserNotFoundException, QueryNotFoundException, QueryStoreInsertException,
+            NotAllowedException {
         final ExecuteStatementDto request = ExecuteStatementDto.builder()
                 .statement(QUERY_5_STATEMENT)
                 .build();
@@ -339,6 +419,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .getData(eq(DATABASE_3_PRIVILEGED_DTO), any(QueryDto.class), eq(null), eq(null));
         when(subsetService.findById(eq(DATABASE_3_PRIVILEGED_DTO), anyLong()))
                 .thenReturn(QUERY_5_DTO);
+        when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID))
+                .thenReturn(DATABASE_3_USER_1_READ_ACCESS_DTO);
         doThrow(SQLException.class)
                 .when(subsetService)
                 .create(eq(DATABASE_3_PRIVILEGED_DTO), eq(QUERY_5_STATEMENT), any(Instant.class), eq(USER_1_ID));
@@ -402,7 +484,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void create_anonymous_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
+    public void create_publicDataPublicSchemaAnonymous_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
             MetadataServiceException, UserNotFoundException, QueryStoreInsertException, TableMalformedException,
             NotAllowedException, SQLException, QueryNotFoundException, DatabaseUnavailableException,
             StorageUnavailableException, QueryMalformedException, QueryNotSupportedException, PaginationException,
@@ -413,19 +495,75 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
                 .build();
 
         /* mock */
-        when(credentialService.getDatabase(DATABASE_3_ID))
-                .thenReturn(DATABASE_3_PRIVILEGED_DTO);
-        when(subsetService.findById(eq(DATABASE_3_PRIVILEGED_DTO), anyLong()))
+        when(credentialService.getDatabase(DATABASE_4_ID))
+                .thenReturn(DATABASE_4_PRIVILEGED_DTO);
+        when(subsetService.findById(eq(DATABASE_4_PRIVILEGED_DTO), anyLong()))
                 .thenReturn(QUERY_5_DTO);
-        when(subsetService.getData(eq(DATABASE_3_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L)))
+        when(subsetService.getData(eq(DATABASE_4_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L)))
                 .thenReturn(mock);
-        when(schemaService.inspectView(eq(DATABASE_3_PRIVILEGED_DTO), anyString()))
+        when(schemaService.inspectView(eq(DATABASE_4_PRIVILEGED_DTO), anyString()))
                 .thenReturn(VIEW_5_DTO);
         when(httpServletRequest.getMethod())
                 .thenReturn("POST");
 
         /* test */
-        subsetEndpoint.create(DATABASE_3_ID, request, null, httpServletRequest, null, null, null);
+        subsetEndpoint.create(DATABASE_4_ID, request, null, httpServletRequest, null, null, null);
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void create_privateDataPrivateSchema_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException,
+            MetadataServiceException, UserNotFoundException, QueryStoreInsertException, TableMalformedException,
+            NotAllowedException, SQLException, QueryNotFoundException, DatabaseUnavailableException,
+            StorageUnavailableException, QueryMalformedException, QueryNotSupportedException, PaginationException,
+            StorageNotFoundException, TableNotFoundException, ViewMalformedException, ViewNotFoundException {
+        final Dataset<Row> mock = sparkSession.emptyDataFrame();
+        final ExecuteStatementDto request = ExecuteStatementDto.builder()
+                .statement(QUERY_1_STATEMENT)
+                .build();
+
+        /* mock */
+        when(credentialService.getDatabase(DATABASE_1_ID))
+                .thenReturn(DATABASE_1_PRIVILEGED_DTO);
+        when(subsetService.findById(eq(DATABASE_1_PRIVILEGED_DTO), anyLong()))
+                .thenReturn(QUERY_1_DTO);
+        when(subsetService.getData(eq(DATABASE_1_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L)))
+                .thenReturn(mock);
+        when(schemaService.inspectView(eq(DATABASE_1_PRIVILEGED_DTO), anyString()))
+                .thenReturn(VIEW_1_DTO);
+        when(httpServletRequest.getMethod())
+                .thenReturn("POST");
+
+        /* test */
+        subsetEndpoint.create(DATABASE_1_ID, request, USER_1_PRINCIPAL, httpServletRequest, null, null, null);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void create_privateDataPublicSchemaAnonymous_fails() throws DatabaseNotFoundException, SQLException,
+            MetadataServiceException, UserNotFoundException, QueryNotFoundException, QueryMalformedException,
+            TableNotFoundException, ViewMalformedException, ViewNotFoundException, RemoteUnavailableException {
+        final Dataset<Row> mock = sparkSession.emptyDataFrame();
+        final ExecuteStatementDto request = ExecuteStatementDto.builder()
+                .statement(QUERY_5_STATEMENT)
+                .build();
+
+        /* mock */
+        when(credentialService.getDatabase(DATABASE_2_ID))
+                .thenReturn(DATABASE_2_PRIVILEGED_DTO);
+        when(subsetService.findById(eq(DATABASE_2_PRIVILEGED_DTO), anyLong()))
+                .thenReturn(QUERY_2_DTO);
+        when(subsetService.getData(eq(DATABASE_2_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L)))
+                .thenReturn(mock);
+        when(schemaService.inspectView(eq(DATABASE_2_PRIVILEGED_DTO), anyString()))
+                .thenReturn(VIEW_4_DTO);
+        when(httpServletRequest.getMethod())
+                .thenReturn("POST");
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            subsetEndpoint.create(DATABASE_2_ID, request, null, httpServletRequest, null, null, null);
+        });
     }
 
     @Test
@@ -687,9 +825,9 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
-    protected List<QueryDto> generic_list(Long databaseId, PrivilegedDatabaseDto database) throws NotAllowedException,
-            DatabaseUnavailableException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException,
-            MetadataServiceException {
+    protected List<QueryDto> generic_list(Long databaseId, PrivilegedDatabaseDto database, Principal principal)
+            throws NotAllowedException, DatabaseUnavailableException, QueryNotFoundException, DatabaseNotFoundException,
+            RemoteUnavailableException, MetadataServiceException {
 
         /* mock */
         if (database != null) {
@@ -702,22 +840,19 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest {
         }
 
         /* test */
-        final ResponseEntity<List<QueryDto>> response = subsetEndpoint.list(databaseId, null);
+        final ResponseEntity<List<QueryDto>> response = subsetEndpoint.list(databaseId, null, principal);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         return response.getBody();
     }
 
-    protected void generic_findById(Long subsetId, MediaType accept, Instant timestamp) throws UserNotFoundException,
-            DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, QueryNotFoundException,
+    protected void generic_findById(Long databaseId, Long subsetId, String accept, Instant timestamp,
+                                    Principal principal) throws UserNotFoundException, DatabaseUnavailableException,
+            StorageUnavailableException, NotAllowedException, QueryMalformedException, QueryNotFoundException,
             DatabaseNotFoundException, RemoteUnavailableException, FormatNotAvailableException,
-            MetadataServiceException, TableNotFoundException, ViewMalformedException, SQLException {
-
-        /* mock */
-        when(mockHttpServletRequest.getHeader("Accept"))
-                .thenReturn(accept.toString());
+            MetadataServiceException, TableNotFoundException, ViewMalformedException {
 
         /* test */
-        final ResponseEntity<?> response = subsetEndpoint.findById(DATABASE_3_ID, subsetId, mockHttpServletRequest, timestamp);
+        final ResponseEntity<?> response = subsetEndpoint.findById(databaseId, subsetId, accept, timestamp, principal);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getBody());
     }
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
index ed720ac930565c0c1f97b291058d4aa293ba0061..e3171892a0be41f816feb749990a70aafc252f3d 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java
@@ -165,9 +165,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void statistic_succeeds() throws DatabaseUnavailableException, TableNotFoundException,
-            TableMalformedException, DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException,
-            SQLException {
+    public void statistic_succeeds() throws DatabaseUnavailableException, TableNotFoundException, SQLException,
+            TableMalformedException, RemoteUnavailableException, MetadataServiceException {
 
         /* mock */
         when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID))
@@ -454,10 +453,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .createTuple(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.CREATED, response.getStatusCode());
     }
 
@@ -473,7 +472,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -495,7 +494,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -518,7 +517,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -545,7 +544,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -568,7 +567,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO);
 
         /* test */
-        tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
     }
 
     @Test
@@ -590,7 +589,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -613,7 +612,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO);
 
         /* test */
-        tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+        tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
     }
 
     @Test
@@ -641,10 +640,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .updateTuple(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
@@ -663,7 +662,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -688,7 +687,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -714,7 +713,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -743,7 +742,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -772,10 +771,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .updateTuple(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
@@ -801,7 +800,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -830,10 +829,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .updateTuple(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
@@ -858,10 +857,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .deleteTuple(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
@@ -876,7 +875,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -897,7 +896,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -919,7 +918,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -944,7 +943,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -969,10 +968,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .deleteTuple(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
@@ -994,7 +993,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1019,10 +1018,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .deleteTuple(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
@@ -1269,10 +1268,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .importDataset(TABLE_8_PRIVILEGED_DTO, request);
         doNothing()
                 .when(metadataServiceGateway)
-                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID);
+                .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN);
 
         /* test */
-        final ResponseEntity<Void> response = tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        final ResponseEntity<Void> response = tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
@@ -1287,7 +1286,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_4_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_4_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1308,7 +1307,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1334,7 +1333,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(DatabaseUnavailableException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1360,7 +1359,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1382,7 +1381,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1404,7 +1403,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO);
 
         /* test */
-        tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
     }
 
     @Test
@@ -1425,7 +1424,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1447,7 +1446,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO);
 
         /* test */
-        tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL);
+        tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
     }
 
     @Test
@@ -1468,7 +1467,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1_USER_2_WRITE_ALL_ACCESS_DTO);
 
         /* test */
-        tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL);
+        tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN);
     }
 
     @Test
@@ -1489,7 +1488,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO);
 
         /* test */
-        tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL);
+        tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN);
     }
 
     @Test
@@ -1510,7 +1509,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -1532,7 +1531,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         });
     }
 
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java
index 720e0652b3ee7f9dc0f30eb9ec014efe76275009..f65de0707eec016f502d66f45e8f52b5811f042f 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java
@@ -31,8 +31,7 @@ import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.when;
 
@@ -45,6 +44,10 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     @Qualifier("restTemplate")
     private RestTemplate restTemplate;
 
+    @MockBean
+    @Qualifier("internalRestTemplate")
+    private RestTemplate internalRestTemplate;
+
     @Autowired
     private MetadataServiceGateway metadataServiceGateway;
 
@@ -66,7 +69,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Table", TABLE_1_INTERNAL_NAME);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.OK)
                         .headers(headers)
                         .body(TABLE_1_DTO));
@@ -87,7 +90,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class));
 
         /* test */
@@ -101,7 +104,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.ServiceUnavailable.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class));
 
         /* test */
@@ -114,7 +117,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getTableById_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .body(TABLE_1_DTO));
 
@@ -134,7 +137,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
                 headers.add(customHeaders.get(j), "");
             }
             /* mock */
-            when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
+            when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
                     .thenReturn(ResponseEntity.status(HttpStatus.OK)
                             .headers(headers)
                             .body(TABLE_1_DTO));
@@ -157,7 +160,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Table", TABLE_1_INTERNAL_NAME);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.OK)
                         .headers(headers)
                         .build());
@@ -178,7 +181,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Port", "" + CONTAINER_1_PORT);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .headers(headers)
                         .body(DATABASE_1_PRIVILEGED_DTO));
@@ -193,7 +196,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class));
 
         /* test */
@@ -207,7 +210,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class));
 
         /* test */
@@ -220,7 +223,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getDatabaseById_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
@@ -237,7 +240,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.OK)
                         .headers(headers)
                         .build());
@@ -258,7 +261,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
                 headers.add(customHeaders.get(j), "");
             }
             /* mock */
-            when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
+            when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)))
                     .thenReturn(ResponseEntity.status(HttpStatus.OK)
                             .headers(headers)
                             .build());
@@ -276,7 +279,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .headers(headers)
                         .body(CONTAINER_1_DTO));
@@ -291,7 +294,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class));
 
         /* test */
@@ -305,7 +308,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class));
 
         /* test */
@@ -318,7 +321,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getContainerById_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
@@ -338,7 +341,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
                 headers.add(customHeaders.get(j), "");
             }
             /* mock */
-            when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
+            when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
                     .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                             .build());
 
@@ -356,7 +359,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ContainerDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.OK)
                         .headers(headers)
                         .build());
@@ -379,7 +382,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-View", VIEW_1_INTERNAL_NAME);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(ViewDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .headers(headers)
                         .body(VIEW_1_DTO));
@@ -394,7 +397,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class));
 
         /* test */
@@ -408,7 +411,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class));
 
         /* test */
@@ -421,7 +424,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getViewById_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
@@ -441,7 +444,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
                 headers.add(customHeaders.get(j), "");
             }
             /* mock */
-            when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class)))
+            when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class)))
                     .thenReturn(ResponseEntity.status(HttpStatus.OK)
                             .build());
 
@@ -463,7 +466,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Database", DATABASE_1_INTERNALNAME);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .headers(headers)
                         .build());
@@ -478,7 +481,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getUserById_succeeds() throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .body(USER_1_DTO));
 
@@ -492,7 +495,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class));
 
         /* test */
@@ -506,7 +509,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class));
 
         /* test */
@@ -519,7 +522,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getUserById_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
@@ -533,7 +536,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getUserById_emptyBody_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .build());
 
@@ -551,7 +554,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .headers(headers)
                         .body(USER_1_DTO));
@@ -568,7 +571,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class));
 
         /* test */
@@ -582,7 +585,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class));
 
         /* test */
@@ -595,7 +598,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getPrivilegedUserById_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
@@ -615,7 +618,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
                 headers.add(customHeaders.get(j), "");
             }
             /* mock */
-            when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
+            when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
                     .thenReturn(ResponseEntity.status(HttpStatus.OK)
                             .build());
 
@@ -633,7 +636,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
         headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD);
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .headers(headers)
                         .build());
@@ -648,7 +651,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getAccess_succeeds() throws RemoteUnavailableException, NotAllowedException, MetadataServiceException {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .body(DATABASE_1_USER_1_READ_ACCESS_DTO));
 
@@ -661,7 +664,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class));
 
         /* test */
@@ -675,7 +678,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.Forbidden.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class));
 
         /* test */
@@ -689,7 +692,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class));
 
         /* test */
@@ -702,7 +705,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getAccess_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
@@ -716,7 +719,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getAccess_emptyBody_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseAccessDto.class)))
                 .thenReturn(ResponseEntity.ok()
                         .build());
 
@@ -730,7 +733,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getIdentifiers_witSubset_succeeds() throws RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
                 .thenReturn(ResponseEntity.ok()
                         .body(new IdentifierBriefDto[]{IDENTIFIER_1_BRIEF_DTO}));
 
@@ -743,7 +746,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getIdentifiers_succeeds() throws RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
                 .thenReturn(ResponseEntity.ok()
                         .body(new IdentifierBriefDto[]{IDENTIFIER_1_BRIEF_DTO}));
 
@@ -757,7 +760,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class));
 
         /* test */
@@ -771,7 +774,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
+                .when(internalRestTemplate)
                 .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class));
 
         /* test */
@@ -784,7 +787,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getIdentifiers_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
@@ -798,7 +801,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void getIdentifiers_emptyBody_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(IdentifierBriefDto[].class)))
                 .thenReturn(ResponseEntity.ok()
                         .build());
 
@@ -809,15 +812,16 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    public void updateTableStatistics_succeeds() throws RemoteUnavailableException, MetadataServiceException, TableNotFoundException {
+    public void updateTableStatistics_succeeds() throws RemoteUnavailableException, MetadataServiceException,
+            TableNotFoundException {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), eq(HttpEntity.EMPTY), eq(Void.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
                 .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
-        metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID);
+        metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN);
     }
 
     @Test
@@ -825,12 +829,12 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpServerErrorException.class)
-                .when(restTemplate)
-                .exchange(anyString(), eq(HttpMethod.PUT), eq(HttpEntity.EMPTY), eq(Void.class));
+                .when(internalRestTemplate)
+                .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class));
 
         /* test */
         assertThrows(RemoteUnavailableException.class, () -> {
-            metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID);
+            metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -839,12 +843,12 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         doThrow(HttpClientErrorException.NotFound.class)
-                .when(restTemplate)
-                .exchange(anyString(), eq(HttpMethod.PUT), eq(HttpEntity.EMPTY), eq(Void.class));
+                .when(internalRestTemplate)
+                .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class));
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID);
+            metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN);
         });
     }
 
@@ -852,13 +856,13 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest {
     public void updateTableStatistics_statusCode_fails() {
 
         /* mock */
-        when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), eq(HttpEntity.EMPTY), eq(Void.class)))
+        when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
                 .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT)
                         .build());
 
         /* test */
         assertThrows(MetadataServiceException.class, () -> {
-            metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID);
+            metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN);
         });
     }
 
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
index 01bce16b08f84e7ad3901beb143ef0737d9ad571..e48f0c048da2dd6c83934566e6e5d9956838ba62 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
@@ -27,7 +27,6 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.context.TestConfiguration;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Import;
-import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 import org.springframework.test.web.servlet.MockMvc;
@@ -131,7 +130,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
 
         /* mock */
         try {
-            subsetEndpoint.list(DATABASE_1_ID, null);
+            subsetEndpoint.list(DATABASE_1_ID, null, USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
@@ -151,7 +150,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
             /* ignore */
         }
         try {
-            subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, new MockHttpServletRequest(), null);
+            subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, "application/json", null, USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
@@ -176,17 +175,17 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
             /* ignore */
         }
         try {
-            tableEndpoint.insertRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDto.builder().build(), USER_1_PRINCIPAL);
+            tableEndpoint.insertRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         } catch (Exception e) {
             /* ignore */
         }
         try {
-            tableEndpoint.updateRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleUpdateDto.builder().build(), USER_1_PRINCIPAL);
+            tableEndpoint.updateRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleUpdateDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         } catch (Exception e) {
             /* ignore */
         }
         try {
-            tableEndpoint.deleteRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDeleteDto.builder().build(), USER_1_PRINCIPAL);
+            tableEndpoint.deleteRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDeleteDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         } catch (Exception e) {
             /* ignore */
         }
@@ -201,7 +200,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
             /* ignore */
         }
         try {
-            tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, ImportDto.builder().build(), USER_1_PRINCIPAL);
+            tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, ImportDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN);
         } catch (Exception e) {
             /* ignore */
         }
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java
index 6a3851b445cf83b74eaccf1b6a3b86bf61ec03d8..158c6743a4e7bac049cae0211fe764030ab4a3f0 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java
@@ -37,7 +37,7 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest {
     private MockMvc mockMvc;
 
     @Test
-    public void findById_noAcceptHeader_succeeds() throws Exception {
+    public void findById_noAcceptHeader_fails() throws Exception {
 
         /* mock */
         when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID))
@@ -48,11 +48,11 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest {
         /* test */
         this.mockMvc.perform(get("/api/database/" + DATABASE_3_ID + "/subset/" + QUERY_5_ID))
                 .andDo(print())
-                .andExpect(status().isOk());
+                .andExpect(status().isBadRequest());
     }
 
     @Test
-    public void findById_jsonAcceptHeader_succeeds() throws Exception {
+    public void findById_privateDataPublicSchema_jsonAcceptHeader_fails() throws Exception {
 
         /* mock */
         when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID))
@@ -64,6 +64,22 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest {
         this.mockMvc.perform(get("/api/database/" + DATABASE_3_ID + "/subset/" + QUERY_5_ID)
                         .accept(MediaType.APPLICATION_JSON))
                 .andDo(print())
+                .andExpect(status().isForbidden());
+    }
+
+    @Test
+    public void findById_publicDataPublicSchema_jsonAcceptHeader_succeeds() throws Exception {
+
+        /* mock */
+        when(metadataServiceGateway.getDatabaseById(DATABASE_4_ID))
+                .thenReturn(DATABASE_4_PRIVILEGED_DTO);
+        when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_7_ID))
+                .thenReturn(QUERY_5_DTO);
+
+        /* test */
+        this.mockMvc.perform(get("/api/database/" + DATABASE_4_ID + "/subset/" + QUERY_7_ID)
+                        .accept(MediaType.APPLICATION_JSON))
+                .andDo(print())
                 .andExpect(status().isOk());
     }
 
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java
index d5cf6dd22e3cbe341ebe612d246bb716ff5c9f6a..2a0ad624dbbb8399cf9d360148c96bd0524c8453 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java
@@ -358,7 +358,6 @@ public class SchemaServiceIntegrationTest extends AbstractUnitTest {
         assertEquals("not_in_metadata_db2", response.getInternalName());
         assertEquals("not_in_metadata_db2", response.getName());
         assertEquals(DATABASE_1_ID, response.getVdbid());
-        assertEquals(DATABASE_1_ID, response.getDatabase().getId());
         assertEquals(DATABASE_1_OWNER, response.getOwner().getId());
         assertFalse(response.getIsInitialView());
         assertEquals(DATABASE_1_PUBLIC, response.getIsPublic());
@@ -383,20 +382,18 @@ public class SchemaServiceIntegrationTest extends AbstractUnitTest {
         assertEquals(DATABASE_1_ID, column3.getDatabaseId());
     }
 
-    protected static void assertViewColumn(ViewColumnDto column, Long id, Long databaseId, String name,
-                                           String internalName, ColumnTypeDto type, Long size, Long d,
-                                           Boolean nullAllowed, String description) {
-        log.trace("assert column: {}", internalName);
+    protected static void assertViewColumn(ViewColumnDto column, ViewColumnDto other) {
         assertNotNull(column);
-        assertEquals(id, column.getId());
-        assertEquals(databaseId, column.getDatabaseId());
-        assertEquals(name, column.getName());
-        assertEquals(internalName, column.getInternalName());
-        assertEquals(type, column.getColumnType());
-        assertEquals(size, column.getSize());
-        assertEquals(d, column.getD());
-        assertEquals(nullAllowed, column.getIsNullAllowed());
-        assertEquals(description, column.getDescription());
+        assertNotNull(other);
+        assertEquals(column.getId(), other.getId());
+        assertEquals(column.getDatabaseId(), other.getDatabaseId());
+        assertEquals(column.getName(), other.getName());
+        assertEquals(column.getInternalName(), other.getInternalName());
+        assertEquals(column.getColumnType(), other.getColumnType());
+        assertEquals(column.getSize(), other.getSize());
+        assertEquals(column.getD(), other.getD());
+        assertEquals(column.getIsNullAllowed(), other.getIsNullAllowed());
+        assertEquals(column.getDescription(), other.getDescription());
     }
 
     protected static void assertColumn(ColumnDto column, Long id, Long tableId, Long databaseId, String name,
@@ -407,8 +404,6 @@ public class SchemaServiceIntegrationTest extends AbstractUnitTest {
         assertEquals(id, column.getId());
         assertEquals(tableId, column.getTableId());
         assertEquals(databaseId, column.getDatabaseId());
-        assertNotNull(column.getTable());
-        assertEquals(tableId, column.getTable().getId());
         assertEquals(name, column.getName());
         assertEquals(internalName, column.getInternalName());
         assertEquals(type, column.getColumnType());
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java
index 5bfaa043d1ded11c26797b94d0ff2cf0d9e509ba..b5555611c666f60f0af6e2d8a122dc11ec3264dd 100644
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java
+++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java
@@ -55,21 +55,7 @@ public class ViewServiceIntegrationTest extends AbstractUnitTest {
     public void create_succeeds() throws SQLException, ViewMalformedException {
 
         /* test */
-        final ViewDto response = viewService.create(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO);
-        assertEquals(VIEW_1_NAME, response.getName());
-        assertEquals(VIEW_1_INTERNAL_NAME, response.getInternalName());
-        assertEquals(VIEW_1_QUERY, response.getQuery());
-        assertNotNull(response.getQueryHash());
-        assertEquals(DATABASE_1_PUBLIC, response.getIsPublic());
-        final List<ViewColumnDto> columns = response.getColumns();
-        assertEquals(VIEW_1_COLUMNS.size(), columns.size());
-        ViewColumnDto ref = VIEW_1_COLUMNS_DTO.get(0);
-        SchemaServiceIntegrationTest.assertViewColumn(columns.get(0), null, ref.getDatabaseId(), ref.getName(), ref.getInternalName(), ref.getColumnType(), ref.getSize(), ref.getD(), ref.getIsNullAllowed(), ref.getDescription());
-        ref = VIEW_1_COLUMNS_DTO.get(1);
-        SchemaServiceIntegrationTest.assertViewColumn(columns.get(1), null, ref.getDatabaseId(), ref.getName(), ref.getInternalName(), ref.getColumnType(), ref.getSize(), ref.getD(), ref.getIsNullAllowed(), ref.getDescription());
-        ref = VIEW_1_COLUMNS_DTO.get(2);
-        SchemaServiceIntegrationTest.assertViewColumn(columns.get(2), null, ref.getDatabaseId(), ref.getName(), ref.getInternalName(), ref.getColumnType(), ref.getSize(), ref.getD(), ref.getIsNullAllowed(), ref.getDescription());
-
+        viewService.create(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO);
     }
 
     @Test
@@ -81,7 +67,6 @@ public class ViewServiceIntegrationTest extends AbstractUnitTest {
         assertEquals("not_in_metadata_db2", view0.getName());
         assertEquals("not_in_metadata_db2", view0.getInternalName());
         assertEquals(DATABASE_1_ID, view0.getVdbid());
-        assertEquals(DATABASE_1_ID, view0.getDatabase().getId());
         assertEquals(DATABASE_1_OWNER, view0.getOwner().getId());
         assertFalse(view0.getIsInitialView());
         assertEquals(DATABASE_1_PUBLIC, view0.getIsPublic());
diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java
deleted file mode 100644
index 13ddfce8d3c171b79096d2e0d1d05948848a8c86..0000000000000000000000000000000000000000
--- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package at.tuwien.utils;
-
-import at.tuwien.test.BaseTest;
-import org.junit.jupiter.api.Test;
-
-
-import static org.junit.jupiter.api.Assertions.*;
-
-public class UserUtilTest extends BaseTest {
-
-    @Test
-    public void constructor_succeeds() {
-
-        /* test */
-        new UserUtil();
-    }
-
-    @Test
-    public void hasRole_succeeds() {
-        assertTrue(UserUtil.hasRole(USER_1_PRINCIPAL, "find-container"));
-    }
-
-    @Test
-    public void hasRole_principalMissing_fails() {
-        assertFalse(UserUtil.hasRole(null, "find-container"));
-    }
-
-    @Test
-    public void hasRole_roleMissing_fails() {
-        assertFalse(UserUtil.hasRole(USER_1_PRINCIPAL, null));
-    }
-
-    @Test
-    public void getId_succeeds() {
-        assertEquals(USER_1_ID, UserUtil.getId(USER_1_PRINCIPAL));
-    }
-
-    @Test
-    public void getId_principalMissing_fails() {
-        assertNull(UserUtil.getId(null));
-    }
-
-    @Test
-    public void getId_roleMissing_fails() {
-        assertNull(UserUtil.getId(USER_LOCAL_ADMIN_PRINCIPAL));
-    }
-}
diff --git a/dbrepo-data-service/rest-service/src/test/resources/application.properties b/dbrepo-data-service/rest-service/src/test/resources/application.properties
index 3094970e3f8a2cdaaa47107549d2762b517b63bd..a0bb7de2bbbdfb31d85a3980355112185e51ca98 100644
--- a/dbrepo-data-service/rest-service/src/test/resources/application.properties
+++ b/dbrepo-data-service/rest-service/src/test/resources/application.properties
@@ -1,5 +1,5 @@
 # enable local spring profile
-spring.profiles.active=local
+spring.profiles.active=local,junit
 
 # disable discovery
 spring.cloud.discovery.enabled=false
@@ -12,7 +12,7 @@ spring.cloud.config.enabled=false
 dbrepo.credentialCacheTimeout=3
 
 # internal datasource
-spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS FDA;NON_KEYWORDS=value
+spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS DBREPO;NON_KEYWORDS=value
 spring.datasource.driverClassName=org.h2.Driver
 spring.datasource.username=sa
 spring.datasource.password=password
diff --git a/dbrepo-data-service/rest-service/src/test/resources/init/weather_at.sql b/dbrepo-data-service/rest-service/src/test/resources/init/weather_at.sql
new file mode 100644
index 0000000000000000000000000000000000000000..050ad3fb54db0e86b25f487423c8e648f2bd8e39
--- /dev/null
+++ b/dbrepo-data-service/rest-service/src/test/resources/init/weather_at.sql
@@ -0,0 +1,11 @@
+CREATE
+DATABASE weather_at;
+USE
+weather_at;
+
+CREATE TABLE mfcc
+(
+    location VARCHAR(255) PRIMARY KEY,
+    lat      DOUBLE PRECISION NULL,
+    lng      DOUBLE PRECISION NULL
+) WITH SYSTEM VERSIONING COMMENT 'Hello mfcc';
diff --git a/dbrepo-data-service/services/pom.xml b/dbrepo-data-service/services/pom.xml
index 180987ded0cb6336af854b2d610a5a103619a166..666cda76a43da22b1993729bf9df8c58ca4d75e9 100644
--- a/dbrepo-data-service/services/pom.xml
+++ b/dbrepo-data-service/services/pom.xml
@@ -6,18 +6,18 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-data-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>services</artifactId>
     <name>dbrepo-data-service-services</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
             <groupId>at.tuwien</groupId>
             <artifactId>dbrepo-data-service-querystore</artifactId>
-            <version>1.6.0</version>
+            <version>1.6.1</version>
         </dependency>
     </dependencies>
 
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/config/GatewayConfig.java b/dbrepo-data-service/services/src/main/java/at/tuwien/config/GatewayConfig.java
index 4bdb9cb89f9d93d14c34bb318667882e299f0201..c511f2b511ff3cd66d52f63a55ca8ca73ce2a6c9 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/config/GatewayConfig.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/config/GatewayConfig.java
@@ -36,7 +36,7 @@ public class GatewayConfig {
     }
 
     @Bean
-    public RestTemplate restTemplate() {
+    public RestTemplate internalRestTemplate() {
         final RestTemplate restTemplate = new RestTemplate();
         restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(metadataEndpoint));
         restTemplate.getInterceptors()
@@ -44,4 +44,9 @@ public class GatewayConfig {
         return restTemplate;
     }
 
+    @Bean
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+
 }
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java
index 8c86d26bf4d7eb66d496713e2f286fade575aee0..e2851071dad176ca44a9f2d0952a0392101ad2eb 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java
@@ -118,11 +118,13 @@ public interface MetadataServiceGateway {
     /**
      * Update the table statistics in the metadata service.
      *
-     * @param databaseId The database id.
-     * @param tableId    The table id.
+     * @param databaseId    The database id.
+     * @param tableId       The table id.
+     * @param authorization The authorization header.
      * @throws RemoteUnavailableException The remote service is not available and invalid data was returned.
      * @throws TableNotFoundException     The table was not found.
      * @throws MetadataServiceException   The remote service returned invalid data.
      */
-    void updateTableStatistics(Long databaseId, Long tableId) throws TableNotFoundException, MetadataServiceException, RemoteUnavailableException;
+    void updateTableStatistics(Long databaseId, Long tableId, String authorization) throws TableNotFoundException,
+            MetadataServiceException, RemoteUnavailableException;
 }
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java
index 8e63bbb7b6aac5eee92a87c3d1221ff07b00c7e5..7d834992cc73b689bc990b6639061ae1ca2ddb71 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java
@@ -1,6 +1,7 @@
 package at.tuwien.gateway.impl;
 
 import at.tuwien.api.container.ContainerDto;
+import at.tuwien.api.container.image.ImageDto;
 import at.tuwien.api.container.internal.PrivilegedContainerDto;
 import at.tuwien.api.database.DatabaseAccessDto;
 import at.tuwien.api.database.ViewDto;
@@ -11,21 +12,21 @@ import at.tuwien.api.database.table.internal.PrivilegedTableDto;
 import at.tuwien.api.identifier.IdentifierBriefDto;
 import at.tuwien.api.user.UserDto;
 import at.tuwien.api.user.internal.PrivilegedUserDto;
+import at.tuwien.config.GatewayConfig;
 import at.tuwien.exception.*;
 import at.tuwien.gateway.MetadataServiceGateway;
 import at.tuwien.mapper.MetadataMapper;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.*;
 import org.springframework.stereotype.Service;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.HttpServerErrorException;
 import org.springframework.web.client.ResourceAccessException;
 import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.DefaultUriBuilderFactory;
 
 import java.time.Instant;
 import java.util.List;
@@ -36,11 +37,17 @@ import java.util.UUID;
 public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
 
     private final RestTemplate restTemplate;
+    private final RestTemplate internalRestTemplate;
+    private final GatewayConfig gatewayConfig;
     private final MetadataMapper metadataMapper;
 
     @Autowired
-    public MetadataServiceGatewayImpl(RestTemplate restTemplate, MetadataMapper metadataMapper) {
+    public MetadataServiceGatewayImpl(@Qualifier("internalRestTemplate") RestTemplate internalRestTemplate,
+                                      RestTemplate restTemplate, GatewayConfig gatewayConfig,
+                                      MetadataMapper metadataMapper) {
         this.restTemplate = restTemplate;
+        this.internalRestTemplate = internalRestTemplate;
+        this.gatewayConfig = gatewayConfig;
         this.metadataMapper = metadataMapper;
     }
 
@@ -48,8 +55,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
     public PrivilegedContainerDto getContainerById(Long containerId) throws RemoteUnavailableException,
             ContainerNotFoundException, MetadataServiceException {
         final ResponseEntity<ContainerDto> response;
+        final String url = "/api/container/" + containerId;
+        log.debug("get privileged container info from metadata service: {}", url);
         try {
-            response = restTemplate.exchange("/api/container/" + containerId, HttpMethod.GET, HttpEntity.EMPTY,
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY,
                     ContainerDto.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find container with id {}: {}", containerId, e.getMessage());
@@ -85,10 +94,9 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             MetadataServiceException {
         final ResponseEntity<PrivilegedDatabaseDto> response;
         final String url = "/api/database/" + id;
-        log.debug("find privileged database from url: {}", url);
+        log.debug("get privileged database info from metadata service: {}", url);
         try {
-            response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY,
-                    PrivilegedDatabaseDto.class);
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, PrivilegedDatabaseDto.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find database with id {}: {}", id, e.getMessage());
             throw new RemoteUnavailableException("Failed to find database: " + e.getMessage(), e);
@@ -125,9 +133,9 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             RemoteUnavailableException, MetadataServiceException {
         final ResponseEntity<TableDto> response;
         final String url = "/api/database/" + databaseId + "/table/" + id;
-        log.debug("find privileged table from url: {}", url);
+        log.debug("get privileged table info from metadata service: {}", url);
         try {
-            response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, TableDto.class);
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, TableDto.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find table with id {}: {}", id, e.getMessage());
             throw new RemoteUnavailableException("Failed to find table: " + e.getMessage(), e);
@@ -167,9 +175,9 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             ViewNotFoundException, MetadataServiceException {
         final ResponseEntity<ViewDto> response;
         final String url = "/api/database/" + databaseId + "/view/" + id;
-        log.debug("find privileged view from url: {}", url);
+        log.debug("get privileged view info from metadata service: {}", url);
         try {
-            response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, ViewDto.class);
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, ViewDto.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find view with id {}: {}", id, e.getMessage());
             throw new RemoteUnavailableException("Failed to find view: " + e.getMessage(), e);
@@ -193,12 +201,18 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             throw new MetadataServiceException("Failed to find view with id " + id + ": body is empty");
         }
         final PrivilegedViewDto view = metadataMapper.viewDtoToPrivilegedViewDto(response.getBody());
-        view.getDatabase().getContainer().getImage().setJdbcMethod(response.getHeaders().get("X-Type").get(0));
-        view.getDatabase().getContainer().setHost(response.getHeaders().get("X-Host").get(0));
-        view.getDatabase().getContainer().setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0)));
-        view.getDatabase().getContainer().setUsername(response.getHeaders().get("X-Username").get(0));
-        view.getDatabase().getContainer().setPassword(response.getHeaders().get("X-Password").get(0));
-        view.getDatabase().setInternalName(response.getHeaders().get("X-Database").get(0));
+        view.setDatabase(PrivilegedDatabaseDto.builder()
+                .internalName(response.getHeaders().get("X-Database").get(0))
+                .container(PrivilegedContainerDto.builder()
+                        .host(response.getHeaders().get("X-Host").get(0))
+                        .port(Integer.parseInt(response.getHeaders().get("X-Port").get(0)))
+                        .username(response.getHeaders().get("X-Username").get(0))
+                        .password(response.getHeaders().get("X-Password").get(0))
+                        .image(ImageDto.builder()
+                                .jdbcMethod(response.getHeaders().get("X-Type").get(0))
+                                .build())
+                        .build())
+                .build());
         view.setInternalName(response.getHeaders().get("X-View").get(0));
         view.setLastRetrieved(Instant.now());
         return view;
@@ -208,8 +222,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
     public UserDto getUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException,
             MetadataServiceException {
         final ResponseEntity<UserDto> response;
+        final String url = "/api/user/" + userId;
+        log.debug("get user info from metadata service: {}", url);
         try {
-            response = restTemplate.exchange("/api/user/" + userId, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class);
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find user with id {}: {}", userId, e.getMessage());
             throw new RemoteUnavailableException("Failed to find user: " + e.getMessage(), e);
@@ -232,8 +248,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
     public PrivilegedUserDto getPrivilegedUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException,
             MetadataServiceException {
         final ResponseEntity<UserDto> response;
+        final String url = "/api/user/" + userId;
+        log.debug("get privileged user info from metadata service: {}", url);
         try {
-            response = restTemplate.exchange("/api/user/" + userId, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class);
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find user with id {}: {}", userId, e.getMessage());
             throw new RemoteUnavailableException("Failed to find user: " + e.getMessage(), e);
@@ -267,8 +285,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
     public DatabaseAccessDto getAccess(Long databaseId, UUID userId) throws RemoteUnavailableException,
             NotAllowedException, MetadataServiceException {
         final ResponseEntity<DatabaseAccessDto> response;
+        final String url = "/api/database/" + databaseId + "/access/" + userId;
+        log.debug("get database access from metadata service: {}", url);
         try {
-            response = restTemplate.exchange("/api/database/" + databaseId + "/access/" + userId, HttpMethod.GET, HttpEntity.EMPTY, DatabaseAccessDto.class);
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, DatabaseAccessDto.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find database access for user with id {}: {}", userId, e.getMessage());
             throw new RemoteUnavailableException("Failed to find database access: " + e.getMessage(), e);
@@ -292,9 +312,9 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             RemoteUnavailableException, DatabaseNotFoundException {
         final ResponseEntity<IdentifierBriefDto[]> response;
         final String url = "/api/identifier?dbid=" + databaseId + (subsetId != null ? ("&qid=" + subsetId) : "");
-        log.trace("mapped url: {}", url);
+        log.debug("get identifiers from metadata service: {}", url);
         try {
-            response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, IdentifierBriefDto[].class);
+            response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, IdentifierBriefDto[].class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to find identifiers for database with id {} and subset with id {}: {}", databaseId, subsetId, e.getMessage());
             throw new RemoteUnavailableException("Failed to find identifiers: " + e.getMessage(), e);
@@ -314,13 +334,16 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
     }
 
     @Override
-    public void updateTableStatistics(Long databaseId, Long tableId) throws TableNotFoundException, MetadataServiceException,
-            RemoteUnavailableException {
+    public void updateTableStatistics(Long databaseId, Long tableId, String authorization) throws TableNotFoundException,
+            MetadataServiceException, RemoteUnavailableException {
         final ResponseEntity<Void> response;
         final String url = "/api/database/" + databaseId + "/table/" + tableId + "/statistic";
-        log.trace("mapped url: {}", url);
+        log.debug("update table statistics in metadata service: {}", url);
+        internalRestTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(gatewayConfig.getMetadataEndpoint()));
+        final HttpHeaders headers = new HttpHeaders();
+        headers.set("Authorization", authorization);
         try {
-            response = restTemplate.exchange(url, HttpMethod.PUT, HttpEntity.EMPTY, Void.class);
+            response = internalRestTemplate.exchange(url, HttpMethod.PUT, new HttpEntity<>(null, headers), Void.class);
         } catch (ResourceAccessException | HttpServerErrorException e) {
             log.error("Failed to update table statistic for table with id {}: {}", tableId, e.getMessage());
             throw new RemoteUnavailableException("Failed to update table statistic: " + e.getMessage(), e);
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java
index b89e6b9c0feb3a282c38afbf39f07ef2d85fb546..5b96a5934b83e2c4636590f7515f70c04aef5826 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java
@@ -33,7 +33,10 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
-import java.sql.*;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
@@ -87,14 +90,13 @@ public interface DataMapper {
      * @param database  The database.
      * @param resultSet The inspected schema.
      * @return The database containing the updated view/table.
-     * @throws SQLException
+     * @throws SQLException If the result set does not contain the requested parameter.
      */
     default ViewDto schemaResultSetToView(DatabaseDto database, ResultSet resultSet) throws SQLException {
         return ViewDto.builder()
                 .name(resultSet.getString(1))
                 .internalName(resultSet.getString(1))
                 .vdbid(database.getId())
-                .database(database)
                 .isInitialView(false)
                 .isPublic(database.getIsPublic())
                 .query(resultSet.getString(9))
@@ -124,7 +126,7 @@ public interface DataMapper {
         return statistic;
     }
 
-    default TableDto resultSetToTable(ResultSet resultSet, TableDto table, QueryConfig queryConfig) throws SQLException {
+    default TableDto resultSetToTable(ResultSet resultSet, TableDto table) throws SQLException {
         final ColumnDto column = ColumnDto.builder()
                 .ordinalPosition(resultSet.getInt(1) - 1) /* start at zero */
                 .isNullAllowed(resultSet.getString(3).equals("YES"))
@@ -132,7 +134,6 @@ public interface DataMapper {
                 .d(resultSet.getString(7) != null ? resultSet.getLong(7) : null)
                 .name(resultSet.getString(10))
                 .internalName(resultSet.getString(10))
-                .table(table)
                 .tableId(table.getId())
                 .databaseId(table.getTdbid())
                 .description(resultSet.getString(11))
@@ -181,7 +182,7 @@ public interface DataMapper {
                 .d(resultSet.getString(7) != null ? resultSet.getLong(7) : null)
                 .name(resultSet.getString(10))
                 .internalName(resultSet.getString(10))
-                .databaseId(view.getDatabase().getId())
+                .databaseId(view.getVdbid())
                 .build();
         /* fix boolean and set size for others */
         if (resultSet.getString(8).equalsIgnoreCase("tinyint(1)")) {
@@ -193,7 +194,7 @@ public interface DataMapper {
         }
         view.getColumns()
                 .add(column);
-        log.trace("parsed view {}.{} column: {}", view.getDatabase().getInternalName(), view.getInternalName(), column.getInternalName());
+        log.trace("parsed view column: {}.{}", view.getInternalName(), column.getInternalName());
         return view;
     }
 
@@ -320,7 +321,7 @@ public interface DataMapper {
         if (!resultSet.next()) {
             throw new TableNotFoundException("Failed to find table in the information schema");
         }
-        final TableDto table = TableDto.builder()
+        return TableDto.builder()
                 .name(resultSet.getString(1))
                 .internalName(resultSet.getString(1))
                 .isVersioned(resultSet.getString(2).equals("SYSTEM VERSIONED"))
@@ -343,7 +344,6 @@ public interface DataMapper {
                         .build())
                 .isPublic(database.getIsPublic())
                 .build();
-        return table;
     }
 
     default void prepareStatementWithColumnTypeObject(PreparedStatement ps, ColumnTypeDto columnType, int idx, Object value) throws SQLException {
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java
index dc073e855d7505d3bdf9d269040af58040d07174..e2b0c984e0f53c68bf9d384a301ab62ad67bbff7 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java
@@ -5,7 +5,6 @@ import at.tuwien.api.database.ViewDto;
 import at.tuwien.api.database.internal.PrivilegedDatabaseDto;
 import at.tuwien.api.database.table.TableDto;
 import at.tuwien.api.database.table.constraints.unique.UniqueDto;
-import at.tuwien.config.QueryConfig;
 import at.tuwien.exception.TableNotFoundException;
 import at.tuwien.exception.ViewNotFoundException;
 import at.tuwien.mapper.DataMapper;
@@ -28,15 +27,12 @@ import java.util.LinkedList;
 public class SchemaServiceMariaDbImpl extends HibernateConnector implements SchemaService {
 
     private final DataMapper dataMapper;
-    private final QueryConfig queryConfig;
     private final MariaDbMapper mariaDbMapper;
     private final MetadataMapper metadataMapper;
 
     @Autowired
-    public SchemaServiceMariaDbImpl(DataMapper dataMapper, QueryConfig queryConfig, MariaDbMapper mariaDbMapper,
-                                    MetadataMapper metadataMapper) {
+    public SchemaServiceMariaDbImpl(DataMapper dataMapper, MariaDbMapper mariaDbMapper, MetadataMapper metadataMapper) {
         this.dataMapper = dataMapper;
-        this.queryConfig = queryConfig;
         this.mariaDbMapper = mariaDbMapper;
         this.metadataMapper = metadataMapper;
     }
@@ -65,7 +61,7 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche
             final ResultSet resultSet2 = statement2.executeQuery();
             log.trace("executed statement in {} ms", System.currentTimeMillis() - start);
             while (resultSet2.next()) {
-                table = dataMapper.resultSetToTable(resultSet2, table, queryConfig);
+                table = dataMapper.resultSetToTable(resultSet2, table);
             }
             /* obtain check constraints metadata */
             start = System.currentTimeMillis();
@@ -97,10 +93,8 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche
                     final TableDto tmpTable = table;
                     uk.getColumns()
                             .forEach(column -> {
-                                column.setTable(tmpTable);
                                 column.setTableId(tmpTable.getId());
                                 column.setDatabaseId(database.getId());
-                                column.setIsPublic(database.getIsPublic());
                             });
                 }
             }
@@ -109,7 +103,6 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche
             final TableDto tmpTable = table;
             tmpTable.getColumns()
                     .forEach(column -> {
-                        column.setTable(tmpTable);
                         column.setTableId(tmpTable.getId());
                         column.setDatabaseId(database.getId());
                     });
@@ -139,7 +132,6 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche
                 throw new ViewNotFoundException("Failed to find view in the information schema");
             }
             ViewDto view = dataMapper.schemaResultSetToView(database, resultSet1);
-            view.setDatabase(database);
             view.setVdbid(database.getId());
             view.setOwner(database.getOwner());
             /* obtain view columns */
@@ -154,7 +146,7 @@ public class SchemaServiceMariaDbImpl extends HibernateConnector implements Sche
                     .columns(new LinkedList<>())
                     .build();
             while (resultSet2.next()) {
-                tmp = dataMapper.resultSetToTable(resultSet2, tmp, queryConfig);
+                tmp = dataMapper.resultSetToTable(resultSet2, tmp);
             }
             view.setColumns(tmp.getColumns()
                     .stream()
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java
index 4d688393cb8b0aa3054bea77c9ad0cc534aa5e65..fb244bb30113ab90f4f2209bf4e80b5d11cd9185 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java
@@ -90,13 +90,14 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs
     @Override
     public Dataset<Row> getData(PrivilegedDatabaseDto database, QueryDto subset, Long page, Long size)
             throws ViewMalformedException, SQLException, QueryMalformedException, TableNotFoundException {
-        if (!viewService.existsByName(database, metadataMapper.queryDtoToViewName(subset))) {
-            log.warn("Missing internal view {} for subset with id {}: create it from subset query", metadataMapper.queryDtoToViewName(subset), subset.getId());
+        final String viewName = metadataMapper.queryDtoToViewName(subset);
+        if (!viewService.existsByName(database, viewName)) {
+            log.warn("Missing internal view {} for subset with id {}: create it from subset query", viewName, subset.getId());
             viewService.create(database, subset);
         } else {
-            log.debug("internal view {} for subset with id {} exists", metadataMapper.queryDtoToViewName(subset), subset.getId());
+            log.debug("internal view {}.{} for subset with id {} exists", database.getInternalName(), viewName, subset.getId());
         }
-        return tableService.getData(database, metadataMapper.queryDtoToViewName(subset), subset.getExecution(), page, size, null, null);
+        return tableService.getData(database, viewName, subset.getExecution(), page, size, null, null);
     }
 
     @Override
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java
index c8da7fd688702c0c69091c9ee27fd972da73b98a..c34f057e0129bd1c65277648adf3b5985952235c 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java
@@ -170,7 +170,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table
             final long start = System.currentTimeMillis();
             final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.tableNameToUpdateTableRawQuery(table.getInternalName()));
             log.trace("prepare with arg 1={}", data.getDescription());
-            statement.setString(1, data.getDescription());
+            if (data.getDescription() == null) {
+                statement.setString(1, "");
+            } else {
+                statement.setString(1, data.getDescription());
+            }
             statement.executeUpdate();
             log.debug("executed statement in {} ms", System.currentTimeMillis() - start);
             connection.commit();
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java
index 989fe4e1a1de03502616d02e74e5aba99e6544af..d85bdc53ac235a3a95bb8b3543bddcfdd143dc6d 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java
@@ -141,7 +141,6 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe
                 .identifiers(new LinkedList<>())
                 .isInitialView(false)
                 .vdbid(database.getId())
-                .database(metadataMapper.privilegedDatabaseDtoToDatabaseDto(database))
                 .columns(new LinkedList<>())
                 .build();
         try {
diff --git a/dbrepo-metadata-db/1_setup-schema.sql b/dbrepo-metadata-db/1_setup-schema.sql
index 92f2b1721e33cf68c00a62169dc0128606bea4b7..c9ce89d1be71f4791c5e55dbb7c24f46e979355a 100644
--- a/dbrepo-metadata-db/1_setup-schema.sql
+++ b/dbrepo-metadata-db/1_setup-schema.sql
@@ -9,6 +9,7 @@ CREATE TABLE IF NOT EXISTS `mdb_users`
     email            character varying(255) NOT NULL,
     orcid            character varying(255),
     affiliation      character varying(255),
+    is_internal      BOOLEAN                NOT NULL DEFAULT FALSE,
     mariadb_password character varying(255) NOT NULL,
     theme            character varying(255) NOT NULL default ('light'),
     language         character varying(3)   NOT NULL default ('en'),
diff --git a/dbrepo-metadata-db/migration/schema_1.5.0-to-1.6.0.sql b/dbrepo-metadata-db/migration/schema_1.5.0-to-1.6.0.sql
index c04efaa1bbf7a9987ab845381a775fb7fd7c1940..be4e88a9c1489eb1554d62d44253c225ea7d427d 100644
--- a/dbrepo-metadata-db/migration/schema_1.5.0-to-1.6.0.sql
+++ b/dbrepo-metadata-db/migration/schema_1.5.0-to-1.6.0.sql
@@ -15,12 +15,16 @@ ALTER TABLE `mdb_tables`
     DROP SYSTEM VERSIONING;
 ALTER TABLE `mdb_tables`
     ADD COLUMN `is_schema_public` BOOLEAN NOT NULL DEFAULT TRUE;
+ALTER TABLE `mdb_tables`
+    ADD COLUMN `is_public` BOOLEAN NOT NULL DEFAULT TRUE;
 ALTER TABLE `mdb_tables`
     DROP FOREIGN KEY `mdb_tables_ibfk_2`;
 ALTER TABLE `mdb_tables`
     DROP COLUMN `created_by`;
-UPDATE `mdb_tables`
-SET `is_schema_public` = `is_public`;
+UPDATE `mdb_tables` t
+SET `is_schema_public` = (SELECT d.is_public FROM mdb_databases d where d.id = t.tDBID);
+UPDATE `mdb_tables` t
+SET `is_schema_public` = (SELECT d.is_public FROM mdb_databases d where d.id = t.tDBID);
 ALTER TABLE `mdb_tables`
     ADD SYSTEM VERSIONING;
 
diff --git a/dbrepo-metadata-service/api/pom.xml b/dbrepo-metadata-service/api/pom.xml
index 8e77c818a4f1c0b353a854636b1f437edf4d5546..9baf18ff69870ee00658f67dc5fa5e43bab49581 100644
--- a/dbrepo-metadata-service/api/pom.xml
+++ b/dbrepo-metadata-service/api/pom.xml
@@ -6,18 +6,18 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-metadata-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-api</artifactId>
     <name>dbrepo-metadata-service-api</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
             <groupId>at.tuwien</groupId>
             <artifactId>dbrepo-metadata-service-entities</artifactId>
-            <version>1.6.0</version>
+            <version>1.6.1</version>
             <scope>compile</scope>
         </dependency>
     </dependencies>
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/ExportResourceDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/ExportResourceDto.java
index 7324094f4c373916d7026d7edf80a50a23c976f6..5c5bf22005be92dbd172bfa4abd580dd298d6d5c 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/ExportResourceDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/ExportResourceDto.java
@@ -7,6 +7,7 @@ import org.springframework.core.io.InputStreamResource;
 @Setter
 @ToString
 @Builder
+@EqualsAndHashCode
 @AllArgsConstructor
 @NoArgsConstructor
 public class ExportResourceDto {
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ChannelDetailsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ChannelDetailsDto.java
index ed521fccdf83f9c9ffcbdb69081624c18c863609..03aeb19ab41e375822af6741e61b3161d67a7591 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ChannelDetailsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ChannelDetailsDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ConsumerDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ConsumerDto.java
index 9973c875e83cf24f69100ea6c52bd28a260d842a..ad82492d7d4d867c4c706bfbc1f1549e9fafcd95 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ConsumerDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ConsumerDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.amqp;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateExchangeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateExchangeDto.java
index 47adfb26e44fafff8e1f64bde9d72fb1f584c8e2..dffe2c1e0e300fb631d7af34a09e605e1064a462 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateExchangeDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateExchangeDto.java
@@ -1,16 +1,15 @@
 package at.tuwien.api.amqp;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.Parameter;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateUserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateUserDto.java
index fea40fd7ccbbc4d1831593bb10fefa06528e7057..372ce8219b0ee2ac63308c9f8028361d8471cb10 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateUserDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateUserDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateVirtualHostDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateVirtualHostDto.java
index be72924306a6eec019296808e5e11845a605c0a8..b27ea597c20a670df8897834b28bc62fcb95c0f7 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateVirtualHostDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/CreateVirtualHostDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.amqp;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ExchangeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ExchangeDto.java
index 6a6aceef067084ac9fbe6e92f2175d15ecac8011..403a04f00b4d437db1746cca1977989e61a85dc8 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ExchangeDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/ExchangeDto.java
@@ -1,16 +1,15 @@
 package at.tuwien.api.amqp;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import io.swagger.v3.oas.annotations.Parameter;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantExchangePermissionsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantExchangePermissionsDto.java
index 6ed572f96246ec8c078d925602f0aefde871a3a9..054548dbf06e683bcd787109afbff0359b59ca65 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantExchangePermissionsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantExchangePermissionsDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantVirtualHostPermissionsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantVirtualHostPermissionsDto.java
index a00578529ce4b1617382757406989fad2d4d3a73..7e84edb80eb21faafbc4eb0ca87f4c5aa65464e4 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantVirtualHostPermissionsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/GrantVirtualHostPermissionsDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.amqp;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueBriefDto.java
index 2bfcb7efe64b78341ff1b32e9b0fb47f954d5dff..8a266043a1a63f216d2ca0a378d69f11458e6555 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueBriefDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.amqp;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueDto.java
index 27ad5ba2878ff7f59f59fb5b2cb67239361c9da9..acc2091d41a158b07c7b23679be7e7ac1c385fc5 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/QueueDto.java
@@ -1,15 +1,15 @@
 package at.tuwien.api.amqp;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/TopicPermissionDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/TopicPermissionDto.java
index 57fb360e64c377063ff982c7b75d6476c70b5ca5..bdd806f71e1312c05d27f034704297fa7c2bd885 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/TopicPermissionDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/TopicPermissionDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/UserDetailsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/UserDetailsDto.java
index f932dfcf999f15761c13a8de0ed3c45d3bb216e1..a786456efd0945d480ace653457f3e8a284ab78c 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/UserDetailsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/UserDetailsDto.java
@@ -2,14 +2,14 @@ package at.tuwien.api.amqp;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/VirtualHostPermissionDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/VirtualHostPermissionDto.java
index 1cc1bd7f88e47fa14fe632e7129fbf70bcfcb4ce..0602c418da993c7bea9e2e557203128c9772fc8c 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/VirtualHostPermissionDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/amqp/VirtualHostPermissionDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java
index fd76994630fac1b0ebf1c24002aed5f51f823405..0beb8a379491869edef613a594616beb21d1da83 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java
@@ -1,11 +1,10 @@
 package at.tuwien.api.auth;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.List;
@@ -13,6 +12,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CredentialDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CredentialDto.java
index 591b73e8067ed8f722a1102f7216c04cc3a328a2..a9f95ad04d9c5e2ca8c953dc5169f5f0d9250bb0 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CredentialDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CredentialDto.java
@@ -1,15 +1,15 @@
 package at.tuwien.api.auth;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/JwtResponseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/JwtResponseDto.java
index c05f053c3b1de0d0758074161a73555069b25ad4..93d562ca77a81caf86f93a9ec6e96184f706d7ae 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/JwtResponseDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/JwtResponseDto.java
@@ -1,9 +1,8 @@
 package at.tuwien.api.auth;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.List;
@@ -11,6 +10,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/KeycloakErrorDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/KeycloakErrorDto.java
index 9b8ad90ea55d8d4aba75e637e4f9906fcc7e86d9..01c0a2d0348a2facf9009d9b865fd52338f2bab9 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/KeycloakErrorDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/KeycloakErrorDto.java
@@ -11,6 +11,7 @@ import java.io.Serializable;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/LoginRequestDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/LoginRequestDto.java
index 5d0de083d9a6e6612301163033e1b043c6a94f78..809de9881bc424fefe1da8937df03217facb1456 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/LoginRequestDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/LoginRequestDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.auth;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RealmAccessDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RealmAccessDto.java
index bd4bcd27370c1c392efba5f413aaa56a93c58293..b759aff16843ef5cf8cc13466af20c1383599996 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RealmAccessDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RealmAccessDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.auth;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RefreshTokenRequestDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RefreshTokenRequestDto.java
index c774a602805d7b81e15aee1783fda3bad42eb8a9..05b8dfd3b5f51007011ea2f2f0d87e34181aeae4 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RefreshTokenRequestDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/RefreshTokenRequestDto.java
@@ -9,6 +9,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java
index 3cd30bc60fca15117f1173585ba5245d362d9208..c9110e041aaf1187675c58d2365cf63e1f1f2003 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java
@@ -1,17 +1,17 @@
 package at.tuwien.api.auth;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Pattern;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/TokenIntrospectDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/TokenIntrospectDto.java
index a1756e0c9090eb64192be72f9f3b58e6d1efe954..8a2fa624b5794a36304949c3269ac3958beeaee1 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/TokenIntrospectDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/TokenIntrospectDto.java
@@ -2,14 +2,14 @@ package at.tuwien.api.auth;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java
index 4038a39dc608db197ca0572882cca1218b4f6c32..9b1f8fcd4791d018970cb8559b5a7dcd1b1d7210 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java
@@ -14,6 +14,7 @@ import java.time.Instant;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/DataTypeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/DataTypeDto.java
index 312dcf998417f13c6d552dcb25d843516f799f1e..a4215c03928529186468081dfa054d6fd434f820 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/DataTypeDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/DataTypeDto.java
@@ -10,6 +10,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java
index 38adbd6f0d6592a0e802d2bf33147916bbd9f139..880db11fbd9818ed432f8dab7a910c48a51bf72e 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java
@@ -10,6 +10,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java
index ea47c51e477e45359598076478fdce87b6c3ddc3..3baa76171b1ad00ab575a0fafa403c675e16f640 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java
@@ -12,6 +12,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/OperatorDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/OperatorDto.java
index a954bf4a61d52a1d4ee3063cf7187f8865969554..96113b5347e6fcd8d99878154686927da9c978ac 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/OperatorDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/OperatorDto.java
@@ -10,12 +10,15 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
 @ToString
 public class OperatorDto {
 
+    private Long id;
+
     @NotBlank
     @JsonProperty("display_name")
     @Schema(example = "XOR")
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/CrossrefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/CrossrefDto.java
index 8c51a0442aff61f4dbea7ae3302c3dcd8503a1fb..4a689a69ce93dead9cbb6bd27916326a986f22f1 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/CrossrefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/CrossrefDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/form/CrossrefLiteralFormDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/form/CrossrefLiteralFormDto.java
index 99a28ba5f2edb22e723362c466a6e724d551455e..b493cf89ff6ea61df68ef919c6a380b9a81769d2 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/form/CrossrefLiteralFormDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/form/CrossrefLiteralFormDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefLabelDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefLabelDto.java
index d37f005d058265859f22e18b1f588f0c1127d9db..7bdaf0f93117c31dd91017f0e3fcda885060cd54 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefLabelDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefLabelDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefPrefLabelDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefPrefLabelDto.java
index 4073032f251b6c3ce90765ed0ce2a9b779cf4328..a70c161d83752cc338c0e0ccf416cc84a8ee8c8b 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefPrefLabelDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/crossref/label/CrossrefPrefLabelDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java
index 7abc667741cc8ae31f3e0a7dd3e4ef66e8b78ccd..7e929eb74843d7ad5589652c269f59d8bd7db498 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java
@@ -2,21 +2,17 @@
 package at.tuwien.api.database;
 
 import at.tuwien.api.user.UserBriefDto;
-import at.tuwien.api.user.UserDto;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonIgnore;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
-import java.time.Instant;
 import java.util.UUID;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
@@ -25,12 +21,10 @@ public class DatabaseAccessDto {
 
     @NotNull
     @JsonIgnore
-    @ToString.Exclude
     private UUID huserid;
 
     @NotNull
     @JsonIgnore
-    @ToString.Exclude
     private Long hdbid;
 
     @NotNull
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java
index e8456c42ef8d0dc6a464a43be64c940faa46c792..46072e83dc14af22d09923d3e9462a53506aa4f8 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java
@@ -1,9 +1,7 @@
 package at.tuwien.api.database;
 
 import at.tuwien.api.identifier.IdentifierBriefDto;
-import at.tuwien.api.identifier.IdentifierDto;
 import at.tuwien.api.user.UserBriefDto;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
@@ -11,12 +9,13 @@ import jakarta.validation.constraints.NotNull;
 import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
-import java.time.Instant;
 import java.util.List;
+import java.util.UUID;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
@@ -43,14 +42,18 @@ public class DatabaseBriefDto {
     @Schema(example = "true")
     private Boolean isPublic;
 
+    @NotNull
+    @JsonProperty("is_schema_public")
+    @Schema(example = "true")
+    private Boolean isSchemaPublic;
+
     private List<IdentifierBriefDto> identifiers;
 
-    @ToString.Exclude
     @NotNull
     private UserBriefDto contact;
 
     @NotNull
     @JsonProperty("owner_id")
-    private UserBriefDto ownerId;
+    private UUID ownerId;
 
 }
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseCreateDto.java
index a20d9456bbd33addde563579ee03e9f7d17c6f73..f87673764ea55479e86ede7e3f2b70c94cd16b9a 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseCreateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseCreateDto.java
@@ -10,6 +10,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java
index 3343f23b68597b89d1fcd1af177191365ce20d1c..5fc253c4335044a1eaabbb43d02f7ae07510178b 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java
@@ -4,7 +4,6 @@ import at.tuwien.api.container.ContainerBriefDto;
 import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.identifier.IdentifierBriefDto;
 import at.tuwien.api.user.UserBriefDto;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
@@ -12,12 +11,12 @@ import jakarta.validation.constraints.NotNull;
 import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
-import java.time.Instant;
 import java.util.List;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
@@ -62,7 +61,6 @@ public class DatabaseDto {
     @Schema(example = "true")
     private Boolean isSchemaPublic;
 
-    @ToString.Exclude
     @NotNull
     private ContainerBriefDto container;
 
@@ -72,11 +70,9 @@ public class DatabaseDto {
 
     private List<IdentifierBriefDto> subsets;
 
-    @ToString.Exclude
     @NotNull
     private UserBriefDto contact;
 
-    @ToString.Exclude
     @NotNull
     private UserBriefDto owner;
 
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyImageDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyImageDto.java
index 627714f6cb358d1d7d53285f1646d565378553fd..5160ae76bfc3b9fc6445a33ff9c1bb277f9bc4dd 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyImageDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyImageDto.java
@@ -6,6 +6,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyVisibilityDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyVisibilityDto.java
index f5a9aa0961a5e9bb99862354e723701ba1b5ba74..e641deade282e838c478b87f249e9b26676c4972 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyVisibilityDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseModifyVisibilityDto.java
@@ -2,14 +2,14 @@ package at.tuwien.api.database;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseTransferDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseTransferDto.java
index 75a517f4c19ace60ab23002b64fe5b3e14e64345..5a8e6beb1086a996aa77f47ff4f1530b62af00df 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseTransferDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseTransferDto.java
@@ -1,8 +1,7 @@
 package at.tuwien.api.database;
 
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.UUID;
@@ -10,6 +9,7 @@ import java.util.UUID;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LicenseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LicenseDto.java
index 20fdf01de167eb9ec8c7b95ab5be7622f5eb51ff..a6384b3487bf1df7d320c2befa303ad59613d5f1 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LicenseDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LicenseDto.java
@@ -1,15 +1,15 @@
 package at.tuwien.api.database;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LoadFileDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LoadFileDto.java
index 7c4a9a72472e8b36cfe3964262c75c77ef916e10..65f5120d9b7cc78442bd071a6b02cbf70f534a41 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LoadFileDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/LoadFileDto.java
@@ -9,6 +9,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/SubjectModifyDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/SubjectModifyDto.java
index 984f37b790264a2e048d32b8d8b210d5550b4936..a82f6669ed62eac5402e0141d2bf414d01dbc462 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/SubjectModifyDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/SubjectModifyDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.database;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/UpdateDatabaseAccessDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/UpdateDatabaseAccessDto.java
index 8a83c998d267e1c3b64300c8e5cbf115d83f3979..32084c865feafc27fd1ae0a1a534229b347d3538 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/UpdateDatabaseAccessDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/UpdateDatabaseAccessDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java
index e29bfbc109b13b9671b66e94d86d104e2fa2ad23..9b7c9fca89472a956b5d142e69c498c616fce81a 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java
@@ -1,7 +1,5 @@
 package at.tuwien.api.database;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
@@ -9,12 +7,12 @@ import jakarta.validation.constraints.NotNull;
 import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
-import java.time.Instant;
 import java.util.UUID;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java
index 613f9c5e71e54bdc09a8c78752d7a40f0eb0eb88..4005433afedbd5440b939d97bcd874f570a56c29 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java
@@ -1,8 +1,6 @@
 package at.tuwien.api.database;
 
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
-import at.tuwien.api.database.table.columns.concepts.ConceptDto;
-import at.tuwien.api.database.table.columns.concepts.UnitDto;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
@@ -14,6 +12,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
@@ -29,7 +28,7 @@ public class ViewColumnDto {
 
     @NotNull
     @Schema(example = "0")
-    @JsonProperty("ordinal_position")
+    @JsonProperty("ord")
     private Integer ordinalPosition;
 
     @NotBlank
@@ -55,7 +54,7 @@ public class ViewColumnDto {
     private Long length;
 
     @NotNull
-    @JsonProperty("column_type")
+    @JsonProperty("type")
     @Schema(example = "string")
     private ColumnTypeDto columnType;
 
@@ -65,19 +64,10 @@ public class ViewColumnDto {
     @Schema(example = "0")
     private Long d;
 
-    private ConceptDto concept;
-
-    private UnitDto unit;
-
     @Size(max = 2048)
     @Schema(example = "Column comment")
     private String description;
 
-    @NotNull
-    @JsonProperty("is_public")
-    @Schema(example = "true")
-    private Boolean isPublic;
-
     @NotNull
     @JsonProperty("is_null_allowed")
     @Schema(example = "false")
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewCreateDto.java
index e63e6933d5da3e7270cc3145cc613800241db382..142a751ec4cc8eaceee7a9402af2c711d1acaa19 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewCreateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewCreateDto.java
@@ -2,16 +2,16 @@ package at.tuwien.api.database;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Size;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java
index 294bd04bd4ef873ac08cab943d0726f6ba9efaeb..d1ee156e9b95c09a9dd31c1ddf01926818f75879 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java
@@ -2,7 +2,6 @@ package at.tuwien.api.database;
 
 import at.tuwien.api.identifier.IdentifierDto;
 import at.tuwien.api.user.UserBriefDto;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
@@ -10,12 +9,12 @@ import jakarta.validation.constraints.NotNull;
 import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
-import java.time.Instant;
 import java.util.List;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
@@ -29,10 +28,6 @@ public class ViewDto {
     @JsonProperty("database_id")
     private Long vdbid;
 
-    @NotNull
-    @ToString.Exclude
-    private DatabaseDto database;
-
     @NotBlank
     @Schema(example = "Air Quality")
     private String name;
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java
index 3317d40aeebe1dfe1e320ef8c77a6a91af8f7e4c..2335ea39baf1a2e8f1461b56c844409f8e21b207 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java
@@ -64,7 +64,6 @@ public class PrivilegedDatabaseDto extends PrivilegedObjectDto {
     @Schema(example = "true")
     private Boolean isSchemaPublic;
 
-    @ToString.Exclude
     @NotNull
     private PrivilegedContainerDto container;
 
@@ -74,11 +73,9 @@ public class PrivilegedDatabaseDto extends PrivilegedObjectDto {
 
     private List<IdentifierDto> subsets;
 
-    @ToString.Exclude
     @NotNull
     private UserBriefDto contact;
 
-    @ToString.Exclude
     @NotNull
     private UserBriefDto owner;
 
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java
index 87a8805374a1e89e406dc8898394107eec5f9a95..bda575f45d05f21a29a7be3d9da6cdfe1511d855 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java
@@ -31,7 +31,6 @@ public class PrivilegedViewDto extends PrivilegedObjectDto {
     private Long vdbid;
 
     @NotNull
-    @ToString.Exclude
     private PrivilegedDatabaseDto database;
 
     @NotBlank
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ExportDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ExportDto.java
index 1b2b1e03865f982d79d1a226990d6bd45bd07d10..36a13b348770945035292c8bbe32722688f8eea9 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ExportDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ExportDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.database.query;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java
index a50c2d1bdf3e0cce964669dfe6bf779581c2b2b5..39cb6683a1cb2d5250054b8dd555bffbad929fad 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java
@@ -2,15 +2,15 @@ package at.tuwien.api.database.query;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java
index 28f8b61a52cd63df6ad0a750cf9dad28ee04dba8..90a1af28bcf14d64d51eca1db1cc6b46760079fb 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryBriefDto.java
@@ -17,6 +17,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java
index b26a8c3c4ef8dbd4144f373d8d5211f6f4b3ae5d..cd1659e73c6a1e0a13e220e4a83aaaf5b5aacc2f 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java
@@ -16,6 +16,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryPersistDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryPersistDto.java
index a8098191866149b699dc80f9d59ca49f0ed8e294..5bc3ac2054452f007c256917c8eaa6a3288f1a1a 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryPersistDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryPersistDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/SaveStatementDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/SaveStatementDto.java
index 19fd6ffbaf7827b60f63f1a08bd9103363a40e26..876dd5dfdd3c3f6a1e199138dfa7a3bfadc6292f 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/SaveStatementDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/SaveStatementDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.database.query;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java
index 615948486cd07626d9701b03df99852df4cebb9c..29531012f1f2912cc7d7f0c326661cee868ec72d 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java
@@ -1,22 +1,18 @@
 package at.tuwien.api.database.table;
 
-import at.tuwien.api.database.table.columns.ColumnBriefDto;
-import at.tuwien.api.user.UserBriefDto;
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
-import java.util.List;
 import java.util.UUID;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java
index 602eef4e5dcd6ad6155df627afdea411ce1bc73e..7c3defcc0b69dc47e20c4e9aa9931fdfad1143b0 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java
@@ -4,11 +4,10 @@ import at.tuwien.api.database.table.columns.ColumnCreateDto;
 import at.tuwien.api.database.table.constraints.ConstraintsCreateDto;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Size;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.List;
@@ -16,6 +15,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateRawQuery.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateRawQuery.java
index efc3842b2908a514d20ab5af5580c97afc6a163e..ec221ae5f0ac5f1e9063a1cfbfcbcc2813f6ff60 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateRawQuery.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateRawQuery.java
@@ -8,6 +8,7 @@ import java.sql.PreparedStatement;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java
index 2370f45429c10e01b5de09ea3a2a8dd278e2c729..67087d438daedf60cd0236a8f5427310b06464e4 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java
@@ -4,7 +4,6 @@ import at.tuwien.api.database.table.columns.ColumnDto;
 import at.tuwien.api.database.table.constraints.ConstraintsDto;
 import at.tuwien.api.identifier.IdentifierDto;
 import at.tuwien.api.user.UserBriefDto;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
@@ -13,17 +12,16 @@ import jakarta.validation.constraints.Size;
 import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
-import java.time.Instant;
 import java.util.List;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
 @ToString
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
 public class TableDto {
 
     @NotNull
@@ -31,7 +29,6 @@ public class TableDto {
 
     @NotNull
     @JsonProperty("database_id")
-    @EqualsAndHashCode.Include
     private Long tdbid;
 
     @NotBlank
@@ -41,7 +38,6 @@ public class TableDto {
     @NotBlank
     @JsonProperty("internal_name")
     @Schema(example = "air_quality")
-    @EqualsAndHashCode.Include
     private String internalName;
 
     @Schema
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java
index 87dfbd2eef6b121f27febf12357821ce6a585327..35df29430ff652372bb11487ada42ae55c02367c 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableHistoryDto.java
@@ -2,9 +2,8 @@ package at.tuwien.api.database.table;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.time.Instant;
@@ -12,6 +11,7 @@ import java.time.Instant;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableInsertRawQuery.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableInsertRawQuery.java
index ea4d33df5de7391fd4dd940e718c121fbce3dacf..de6ff7feb7f35eeeb82fa8b028409268a473a7ed 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableInsertRawQuery.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableInsertRawQuery.java
@@ -9,6 +9,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java
index 8862723b9ca573a9b7356f207d145850d1c233af..6b5529a0a1d08f75b803575e944cc5e624177eca 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java
@@ -12,6 +12,7 @@ import java.util.Map;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
@@ -35,6 +36,5 @@ public class TableStatisticDto {
     private Long avgRowLength;
 
     @NotNull
-    @ToString.Exclude
     private Map<String, ColumnStatisticDto> columns;
 }
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java
index eb626b0eaf806fab422ffb2c91f0cd38ac8e486c..6c3a946e1257dd2a9a90175a1904d88cf2f1a339 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java
@@ -1,8 +1,7 @@
 package at.tuwien.api.database.table;
 
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.Map;
@@ -10,6 +9,7 @@ import java.util.Map;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java
index a428cb572604815ae80ab9f47f28965a7a784a69..62f57434b67dfe69e95bd212afb795f004da87d7 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java
@@ -1,8 +1,7 @@
 package at.tuwien.api.database.table;
 
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.Map;
@@ -10,6 +9,7 @@ import java.util.Map;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java
index fee00469b127f71361fbfd88013c915038023fae..be50791b944db8eada4316ca6ee975dedf95a113 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java
@@ -1,8 +1,7 @@
 package at.tuwien.api.database.table;
 
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.Map;
@@ -10,6 +9,7 @@ import java.util.Map;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java
index 938315457433287d353a1e5722719d53a776098c..4dee5f0837148f9c2d1095fdbe12d6ab664672ef 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java
@@ -2,15 +2,15 @@ package at.tuwien.api.database.table.columns;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java
index 2d0696abc044db7dbffe8ace7444df0b9f7127c4..ca9bb10569e7bbeb30dd4b8080d0adbbcd2fab75 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java
@@ -2,11 +2,10 @@ package at.tuwien.api.database.table.columns;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Size;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.List;
@@ -14,6 +13,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java
index 92092fca33d726616ccba63f08a4b4fefbdba1a2..1f8d3b046e7c6fc6512fc68e53095822a3513061 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java
@@ -1,10 +1,7 @@
 package at.tuwien.api.database.table.columns;
 
-import at.tuwien.api.database.ViewDto;
-import at.tuwien.api.database.table.TableDto;
-import at.tuwien.api.database.table.columns.concepts.ConceptDto;
-import at.tuwien.api.database.table.columns.concepts.UnitDto;
-import com.fasterxml.jackson.annotation.JsonIgnore;
+import at.tuwien.api.database.table.columns.concepts.ConceptBriefDto;
+import at.tuwien.api.database.table.columns.concepts.UnitBriefDto;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,11 +17,11 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
 @ToString
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
 public class ColumnDto {
 
     @NotNull
@@ -40,7 +37,7 @@ public class ColumnDto {
 
     @NotNull
     @Schema(example = "0")
-    @JsonProperty("ordinal_position")
+    @JsonProperty("ord")
     private Integer ordinalPosition;
 
     @NotBlank
@@ -52,7 +49,6 @@ public class ColumnDto {
     @Size(max = 64)
     @JsonProperty("internal_name")
     @Schema(example = "mdb_date")
-    @EqualsAndHashCode.Include
     private String internalName;
 
     @Schema
@@ -65,7 +61,7 @@ public class ColumnDto {
     private Long length;
 
     @NotNull
-    @JsonProperty("column_type")
+    @JsonProperty("type")
     @Schema(example = "string")
     private ColumnTypeDto columnType;
 
@@ -105,28 +101,14 @@ public class ColumnDto {
     @JsonProperty("std_dev")
     private BigDecimal stdDev;
 
-    private ConceptDto concept;
+    private ConceptBriefDto concept;
 
-    private UnitDto unit;
+    private UnitBriefDto unit;
 
     @Size(max = 2048)
     @Schema(example = "Column comment")
     private String description;
 
-    @ToString.Exclude
-    @JsonIgnore
-    @EqualsAndHashCode.Include
-    private TableDto table;
-
-    @ToString.Exclude
-    @JsonIgnore
-    private transient List<ViewDto> views;
-
-    @NotNull
-    @JsonProperty("is_public")
-    @Schema(example = "true")
-    private Boolean isPublic;
-
     @NotNull
     @JsonProperty("is_null_allowed")
     @Schema(example = "false")
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnStatisticDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnStatisticDto.java
index 3f7ec87f3e92bcbfec1b1d8492874f6a0dd354ef..a7d76a47b260b7eac7e6b2c39a7c768abedbbe3c 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnStatisticDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnStatisticDto.java
@@ -6,11 +6,11 @@ import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.math.BigDecimal;
-import java.util.List;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java
index 77a38f70b46b521e2cf0b03bdba4aca8fe6b48a7..9c74981f1416740ee6e5679d1ea2ed3fbc59e2bc 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptBriefDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e2b36dc66a2f75a846e0ee66cef2037bb8cfd8b
--- /dev/null
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptBriefDto.java
@@ -0,0 +1,28 @@
+package at.tuwien.api.database.table.columns.concepts;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
+import lombok.extern.jackson.Jacksonized;
+
+@Getter
+@Setter
+@Builder
+@EqualsAndHashCode
+@NoArgsConstructor
+@AllArgsConstructor
+@Jacksonized
+@ToString
+public class ConceptBriefDto {
+
+    @NotNull
+    private Long id;
+
+    @NotBlank
+    private String uri;
+
+    private String name;
+
+    private String description;
+
+}
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java
index df171a5ee45c2dd81af2454d9c6a564ed333cfca..da00e64e8362ed298ba2bb9e2e98a596af716431 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptDto.java
@@ -1,21 +1,17 @@
 package at.tuwien.api.database.table.columns.concepts;
 
 import at.tuwien.api.database.table.columns.ColumnBriefDto;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
-import org.springframework.data.annotation.Id;
 
-import java.time.Instant;
 import java.util.List;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java
index 159e07823ce48327a171d976b16e40db5f2fb8fe..b61d911ef9b3141954fe041fa18f8ee9d1362680 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptSaveDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitBriefDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..407f3708eb7a7fbe2b9746122b1f712858b8cf5e
--- /dev/null
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitBriefDto.java
@@ -0,0 +1,27 @@
+package at.tuwien.api.database.table.columns.concepts;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
+import lombok.extern.jackson.Jacksonized;
+
+@Getter
+@Setter
+@Builder
+@EqualsAndHashCode
+@NoArgsConstructor
+@AllArgsConstructor
+@Jacksonized
+@ToString
+public class UnitBriefDto {
+
+    @NotNull
+    private Long id;
+
+    @NotBlank
+    private String uri;
+
+    private String name;
+
+    private String description;
+}
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java
index bd2975c0cc53162fab217c49f8a4a0001f6b047d..48c7180c067123ec4ea09b0eb1b6cd167c4c9d7b 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitDto.java
@@ -1,21 +1,17 @@
 package at.tuwien.api.database.table.columns.concepts;
 
 import at.tuwien.api.database.table.columns.ColumnBriefDto;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
-import org.springframework.data.annotation.Id;
 
-import java.time.Instant;
 import java.util.List;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java
index 326efc48b545a0d1ae55d6d41dfc7256ccb2f162..530c7c8fbb806e8ffc2eac124f0bd6fec3321ea4 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitSaveDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsCreateDto.java
index a317b08b8afb1a5f69d30aa69ff5d68ddbce14d6..3d35a20b0c57ae76f2d5fb6c4843e4d46d43ffab 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsCreateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsCreateDto.java
@@ -12,6 +12,7 @@ import java.util.Set;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java
index 1644c95cdcf488425f5ee6fb795bdac432169d6f..8016de91316bb0a5cbac30fc4148ecc39acdc33b 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java
@@ -28,11 +28,9 @@ public class ForeignKeyDto {
     private List<ForeignKeyReferenceDto> references;
 
     @NotNull
-    @ToString.Exclude
     private TableBriefDto table;
 
     @NotNull
-    @ToString.Exclude
     @JsonProperty("referenced_table")
     private TableBriefDto referencedTable;
 
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java
index 111903d9265517e4b532c46a84983843619f0835..55fcc46ecf85fc097eeef7677b9d0c897c612aa3 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java
@@ -22,11 +22,9 @@ public class ForeignKeyReferenceDto {
     private ForeignKeyBriefDto foreignKey;
 
     @NotNull
-    @ToString.Exclude
     private ColumnBriefDto column;
 
     @NotNull
-    @ToString.Exclude
     @JsonProperty("referenced_column")
     private ColumnBriefDto referencedColumn;
 }
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java
index 25ad3f5d175cc8aeb4d07011c2bc9829da7242a6..80df5d443b70146c2ce2cda6e96bbf75c1816e28 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java
@@ -18,10 +18,8 @@ public class PrimaryKeyDto {
     private Long id;
 
     @NotNull
-    @ToString.Exclude
     private TableBriefDto table;
 
     @NotNull
-    @ToString.Exclude
     private ColumnBriefDto column;
 }
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java
index 05de53784457602248105e8ccd7a6987db53dd51..2bcc9d6cf4fb4d130c398dc2ed0341af46f61df9 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java
@@ -27,10 +27,8 @@ public class UniqueDto {
     private String name;
 
     @NotNull
-    @ToString.Exclude
     private TableBriefDto table;
 
     @NotNull
-    @ToString.Exclude
     private List<ColumnDto> columns;
 }
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java
index 33fe8c4789c3054eed0d7aeb8997cc400da1125f..64b23f17c412c1042c0a1db9a040a67fcc612b39 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java
@@ -24,7 +24,7 @@ import java.util.List;
 @AllArgsConstructor
 @Jacksonized
 @ToString
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+@EqualsAndHashCode
 public class PrivilegedTableDto extends PrivilegedObjectDto {
 
     @NotNull
@@ -32,7 +32,6 @@ public class PrivilegedTableDto extends PrivilegedObjectDto {
 
     @NotNull
     @JsonProperty("database_id")
-    @EqualsAndHashCode.Include
     private Long tdbid;
 
     @NotBlank
@@ -42,7 +41,6 @@ public class PrivilegedTableDto extends PrivilegedObjectDto {
     @NotBlank
     @JsonProperty("internal_name")
     @Schema(example = "air_quality")
-    @EqualsAndHashCode.Include
     private String internalName;
 
     @Schema
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/error/ApiErrorDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/error/ApiErrorDto.java
index c58f152d40f1b24ca2a728460374406f9e217e1f..bb271f5ed6065595898f0a4f73b9758f66da2294 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/error/ApiErrorDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/error/ApiErrorDto.java
@@ -1,15 +1,15 @@
 package at.tuwien.api.error;
 
 import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
 import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 import org.springframework.http.HttpStatus;
 
-import jakarta.validation.constraints.NotNull;
-
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java
index 42675c889e11660d8e98cb2e2fb2c229d2d6850f..9c166f869c5bcec81c1e67212616e03833fe64c7 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java
@@ -13,6 +13,7 @@ import org.springframework.data.annotation.Id;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java
index 616074f23331a1ee0f329e756fe86cd2fe8998e9..55e6ff76214a8af82ec32991c62aa265909d75f6 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java
@@ -11,6 +11,7 @@ import org.springframework.data.annotation.Id;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java
index bff087c5905b817df53f5b338d2cee0284dfa95e..87baf4870661e292dbb69d180cfb1be43ac15f16 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java
@@ -2,7 +2,7 @@ package at.tuwien.api.identifier;
 
 import at.tuwien.api.database.LanguageTypeDto;
 import at.tuwien.api.database.LicenseDto;
-import at.tuwien.api.user.UserDto;
+import at.tuwien.api.user.UserBriefDto;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -13,11 +13,11 @@ import lombok.extern.jackson.Jacksonized;
 
 import java.time.Instant;
 import java.util.List;
-import java.util.UUID;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
@@ -71,7 +71,6 @@ public class IdentifierDto {
     @Schema(description = "query hash in sha512")
     private String queryHash;
 
-    @NotNull
     @Schema(example = "2021-03-12T15:26:21Z")
     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC")
     private Instant execution;
@@ -92,7 +91,7 @@ public class IdentifierDto {
     private String publisher;
 
     @NotNull
-    private UserDto owner;
+    private UserBriefDto owner;
 
     @JsonProperty("publication_day")
     @Schema(example = "15")
@@ -116,8 +115,4 @@ public class IdentifierDto {
 
     private IdentifierStatusTypeDto status;
 
-    @NotNull
-    @JsonProperty("created_by")
-    private UUID createdBy;
-
 }
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java
index ba0cc5b6dd0ce367daf6f71a2f71646325e9b399..921ba3eb983840dee342071bfde0a75f478946b4 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java
@@ -11,6 +11,7 @@ import org.springframework.data.annotation.Id;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java
index 70d6006bc239f2a09829cdb62ffbce950a75c4da..9333a05ce961a403b84463a10abc86ddb2a2e86c 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java
@@ -11,6 +11,7 @@ import org.springframework.data.annotation.Id;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java
index 271333c49dcce9d0be8a16605c6dd89ad11f837e..74525bf711988a0690aeba9c403a44156885b0f3 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java
@@ -16,6 +16,7 @@ import java.time.Instant;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/keycloak/UserCreateAttributesDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/keycloak/UserCreateAttributesDto.java
new file mode 100644
index 0000000000000000000000000000000000000000..6df8ce5e8f733313e9c9f2534e6cef03adb0d5e5
--- /dev/null
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/keycloak/UserCreateAttributesDto.java
@@ -0,0 +1,19 @@
+package at.tuwien.api.keycloak;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import lombok.extern.jackson.Jacksonized;
+
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Jacksonized
+@ToString
+public class UserCreateAttributesDto {
+
+    @JsonProperty("CUSTOM_ID")
+    private String ldapId;
+
+}
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/keycloak/UserCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/keycloak/UserCreateDto.java
index fe4b69550259023b135bf2ae43a70c85c888cce0..2a80811b6247cd18fde5b3eadea1921b1ab2c3a9 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/keycloak/UserCreateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/keycloak/UserCreateDto.java
@@ -1,6 +1,5 @@
 package at.tuwien.api.keycloak;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import lombok.*;
@@ -36,4 +35,6 @@ public class UserCreateDto {
 
     private List<String> groups;
 
+    private UserCreateAttributesDto attributes;
+
 }
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ldap/UserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ldap/UserDto.java
index 3cb0a86e738a994a829fa064a95c5db1ac90b3a3..e6aec082263ebe4ebe2789a683264d06df43cc9b 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ldap/UserDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ldap/UserDto.java
@@ -9,15 +9,14 @@ import java.util.UUID;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
 @ToString
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
 public class UserDto {
 
     @NotNull
-    @EqualsAndHashCode.Include
     private UUID id;
 
     @NotNull
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/maintenance/BannerMessageDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/maintenance/BannerMessageDto.java
index 8143b18fb94f4d62d45339cf09845b72d3f3fcb6..e9c15c212e100f997e6c4afc6c585a4726e92feb 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/maintenance/BannerMessageDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/maintenance/BannerMessageDto.java
@@ -13,6 +13,7 @@ import java.time.Instant;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/OrcidDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/OrcidDto.java
index 4520b692bf65862f8326378b95c045d997c484b0..c4ea89b005df0f5506e91b2bcc428c95a173bc7f 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/OrcidDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/OrcidDto.java
@@ -9,6 +9,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/OrcidActivitiesSummaryDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/OrcidActivitiesSummaryDto.java
index 544754cedf09f14794693da981aa293cc8a73097..625611abd27a28864b310354936f9d167adee12f 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/OrcidActivitiesSummaryDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/OrcidActivitiesSummaryDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/OrcidEmploymentsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/OrcidEmploymentsDto.java
index 5b8b6a3957d03341b91506de9caf6be06089f18d..dea853b62bc3680e8bcefcedb50f85aa567b2c74 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/OrcidEmploymentsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/OrcidEmploymentsDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/OrcidAffiliationGroupDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/OrcidAffiliationGroupDto.java
index 5a4ace0158c436c43d05c6d64aa713f243bc8887..a82a88869bb076791a85e8a9a51f45afb8e3a937 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/OrcidAffiliationGroupDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/OrcidAffiliationGroupDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/OrcidEmploymentSummaryDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/OrcidEmploymentSummaryDto.java
index df3c038abfce62b858f108303eb672a91ea50339..984dc8a6d07fe69b00a49636268e4334f3037678 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/OrcidEmploymentSummaryDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/OrcidEmploymentSummaryDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/OrcidSummaryDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/OrcidSummaryDto.java
index e10e72481eadc20423d718ebcc1da9c8a9ba4297..1377a73dba186cab8fafcda307643ae62144a601 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/OrcidSummaryDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/OrcidSummaryDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/OrcidOrganizationDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/OrcidOrganizationDto.java
index 53c59b4d1a995c0017ae571e0be9afdc44ecc310..149af1b3e693c2bd2da2e2f3decb102ee878e2d0 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/OrcidOrganizationDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/OrcidOrganizationDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/disambiguated/OrcidDisambiguatedDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/disambiguated/OrcidDisambiguatedDto.java
index 5d2e31c5235f3d1fd9fdb81f8d49896e751dbb83..c5ed53e37ff2ddab6a79e76465578d306ce50952 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/disambiguated/OrcidDisambiguatedDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/activities/employments/affiliation/group/summary/organization/disambiguated/OrcidDisambiguatedDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/OrcidPersonDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/OrcidPersonDto.java
index 31c7f9235f5565e3f8e375398ba6c093c61d2036..f7de2794d721f283514562dfd8abecd687c229e5 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/OrcidPersonDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/OrcidPersonDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidNameDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidNameDto.java
index a36f9b044e2242d5349140a897be4d54ff9a06c3..44c2ac0e6bdcc58229b0189709adc20729697669 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidNameDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidNameDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidValueDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidValueDto.java
index baad8b0b782eb373a978b6467c4125080bc62914..251a39c23c3cd8d7dd4eb013461b530e76305647 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidValueDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/orcid/person/name/OrcidValueDto.java
@@ -6,6 +6,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ror/RorDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ror/RorDto.java
index d0c0f54bd53b5e26822d27371d2adc8101e796f7..8e9407885ca4f7e31994836a5664025ce7d499bb 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ror/RorDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/ror/RorDto.java
@@ -6,6 +6,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyBriefDto.java
index 4a3436dabab5dd556b99e0d266dac8535979d353..b908b6e09e26ea5938b7eb0b44554db0a746d22e 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyBriefDto.java
@@ -10,6 +10,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyDto.java
index 09099331ec7cb5a37fd2b3ce8604f59b97005d7b..686cd9f030c952ace5d85a0eecb3f049bcd1a2a9 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/OntologyDto.java
@@ -14,6 +14,7 @@ import java.time.Instant;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java
index 309561b96680b500faf15e79e71dba6d38b92dcc..79ea8d6a5a511a5794df53c455dddd912678219e 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/semantics/TableColumnEntityDto.java
@@ -13,7 +13,7 @@ import lombok.extern.jackson.Jacksonized;
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+@EqualsAndHashCode
 @ToString
 public class TableColumnEntityDto {
 
@@ -33,7 +33,6 @@ public class TableColumnEntityDto {
     private Long columnId;
 
     @NotBlank
-    @EqualsAndHashCode.Include
     @Schema(example = "https://www.wikidata.org/entity/Q1686799")
     private String uri;
 
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/ExchangeUpdatePermissionsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/ExchangeUpdatePermissionsDto.java
index d68514d42fff45a0b0f025a622847b4886906e57..f545f6204187a66a1c8942cb5e8a3eef9d73ae65 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/ExchangeUpdatePermissionsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/ExchangeUpdatePermissionsDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.user;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/GrantedAuthorityDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/GrantedAuthorityDto.java
index 08a7ce10d6a8c118299b771f4225eac0500fc85c..07e0029a716b1d4414b1c10c71b4ad6bcb11ae4a 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/GrantedAuthorityDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/GrantedAuthorityDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserAttributesDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserAttributesDto.java
index 713fbdb0437947a685a08060810b0f7a25c74f92..ef9bae650c9c8615c9f7c43198dd61285aa2ce41 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserAttributesDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserAttributesDto.java
@@ -9,6 +9,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserBriefDto.java
index 08ce389cbfae5b6017ac770f0982a0ca90f904d9..d245bbaf4c46584b9f0cd93fa7b43c2b31ef6333 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserBriefDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserBriefDto.java
@@ -2,9 +2,8 @@ package at.tuwien.api.user;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.UUID;
@@ -12,6 +11,7 @@ import java.util.UUID;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDetailsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDetailsDto.java
index e72a0505ab63963281091714336ec2598f584359..cd5e8fd3e0cefe2f016261470b1236e9a3442b16 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDetailsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDetailsDto.java
@@ -1,17 +1,18 @@
 package at.tuwien.api.user;
 
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotNull;
 import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 
-import jakarta.validation.constraints.Email;
-import jakarta.validation.constraints.NotNull;
 import java.util.List;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java
index 00a866bfd2923cc1ae88abc8273cd2d4b912295e..343d582b55da3af324aa23fd31ef61d8e1cd564d 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java
@@ -2,9 +2,8 @@ package at.tuwien.api.user;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.UUID;
@@ -12,15 +11,14 @@ import java.util.UUID;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
 @ToString
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
 public class UserDto {
 
     @NotNull
-    @EqualsAndHashCode.Include
     @Schema(example = "1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4")
     private UUID id;
 
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserEmailDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserEmailDto.java
index 0459cb96e8911efc6197abb3752e8bb09a0faa2f..caaf33249710f14c7c49540aa8dd2afe0a18e0c3 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserEmailDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserEmailDto.java
@@ -1,15 +1,15 @@
 package at.tuwien.api.user;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserForgotDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserForgotDto.java
index ffc95c3f8a6a0858961e57c5da58f1b5efbddf40..5ebcbae7429c335c122c62e58644b739d8fc90ea 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserForgotDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserForgotDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.user;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.Email;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserModifyPasswordDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserModifyPasswordDto.java
index 5fe224ee77185d2c5601133fcd0e22ec6a2546b9..2b86672cc2704886a287912ff39c70eba63f1904 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserModifyPasswordDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserModifyPasswordDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.user;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserPasswordDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserPasswordDto.java
index bcd21ded0249886fa44ca3e06d8be0725be3c060..dcf53932840057f6916b8ac281eae9031a40677e 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserPasswordDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserPasswordDto.java
@@ -1,13 +1,13 @@
 package at.tuwien.api.user;
 
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserResetDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserResetDto.java
index 919c3b12aff302ef9d1b14a6d287c7108d4bb753..6cac59a904c529b1aa226b788bff427092e5bb1f 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserResetDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserResetDto.java
@@ -1,13 +1,13 @@
 package at.tuwien.api.user;
 
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserRolesDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserRolesDto.java
index 06d7c83f26ffa29fcba90e8cb01993a96c185238..5667f38ec6c6f639981376292af5d16cda58b866 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserRolesDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserRolesDto.java
@@ -1,8 +1,7 @@
 package at.tuwien.api.user;
 
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 import java.util.List;
@@ -10,6 +9,7 @@ import java.util.List;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java
index 17cd44442a0c83d53fd189cad5e216ae832050af..3f5b899df8777459e4df5faa86dccad01d6ac703 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserThemeSetDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.user;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotNull;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java
index 7f536fba36202d9689ec96620df16dfcc22ab672..68d674a7c2287dbd747969d3ad58cdf303cd2bf6 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdateDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdatePermissionsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdatePermissionsDto.java
index f54d2c474999d1d4b3c613c94e761cc584f66034..99f1eab30af6dc537ae767adb4e5e8cc8a1f5048 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdatePermissionsDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserUpdatePermissionsDto.java
@@ -1,14 +1,14 @@
 package at.tuwien.api.user;
 
 import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
 import jakarta.validation.constraints.NotBlank;
+import lombok.*;
 import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/ExternalMetadataDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/ExternalMetadataDto.java
index 80d5d04d6db2ea80de5b1b64c8a9683b51a3dd69..fb40af09487d8b65a2efabb0cb18d46e317484bb 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/ExternalMetadataDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/ExternalMetadataDto.java
@@ -4,11 +4,11 @@ import at.tuwien.api.user.external.affiliation.ExternalAffiliationDto;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.*;
-import lombok.extern.jackson.Jacksonized;
 
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 public class ExternalMetadataDto {
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/affiliation/ExternalAffiliationDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/affiliation/ExternalAffiliationDto.java
index 0e56dea2a2ef7ac7427fa3d561b9efc791f93634..d8d30894bcb443a5bfb33426c01f3910bd4ee9b2 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/affiliation/ExternalAffiliationDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/external/affiliation/ExternalAffiliationDto.java
@@ -8,6 +8,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java
index b0600cfefd8b43b23445c9ccd0e7e418d820ea42..56e24cd81590261137cfa3d4f0f3c45399d80e70 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java
@@ -15,15 +15,14 @@ import java.util.UUID;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
 @ToString
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
 public class PrivilegedUserDto extends PrivilegedObjectDto {
 
     @NotNull
-    @EqualsAndHashCode.Include
     @Schema(example = "1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4")
     private UUID id;
 
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/UpdateUserPasswordDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/UpdateUserPasswordDto.java
index a498dd4a3156c09664bac68b50278f421cd66d58..c89e795bd367739c5b9d371e73c0191fa6758842 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/UpdateUserPasswordDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/UpdateUserPasswordDto.java
@@ -7,6 +7,7 @@ import lombok.extern.jackson.Jacksonized;
 @Getter
 @Setter
 @Builder
+@EqualsAndHashCode
 @NoArgsConstructor
 @AllArgsConstructor
 @Jacksonized
diff --git a/dbrepo-metadata-service/entities/pom.xml b/dbrepo-metadata-service/entities/pom.xml
index 6a8f527f188040713c0f259fb9ade60e7ef5b4dd..5b1c1d0cf133fcfea9de76e37cc0462d318481b6 100644
--- a/dbrepo-metadata-service/entities/pom.xml
+++ b/dbrepo-metadata-service/entities/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-metadata-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-entities</artifactId>
     <name>dbrepo-metadata-service-entity</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies/>
 
diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/image/Operator.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/image/Operator.java
index bbd994fa7ec05d775ba8a7c2eebb1ece418d2726..1ecedc5e16cef533008a28db7d022acab336c8d7 100644
--- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/image/Operator.java
+++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/container/image/Operator.java
@@ -9,11 +9,11 @@ import static jakarta.persistence.GenerationType.IDENTITY;
 @Data
 @Entity
 @Builder
+@EqualsAndHashCode
 @ToString
 @AllArgsConstructor
 @NoArgsConstructor
 @EntityListeners(AuditingEntityListener.class)
-@EqualsAndHashCode
 @Table(name = "mdb_image_operators")
 public class Operator {
 
diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java
index f70fbd93a31d7566e878fd11f31945bc73689c49..d90a702db33c759a511480fa0e89c37efc2773fc 100644
--- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java
+++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java
@@ -32,14 +32,12 @@ import static jakarta.persistence.GenerationType.IDENTITY;
         @UniqueConstraint(columnNames = {"cid", "internalName"})
 })
 @NamedQueries({
-        @NamedQuery(name = "Database.findAllDesc", query = "select d from Database d order by d.created desc"),
-        @NamedQuery(name = "Database.findByInternalName", query = "select d from Database d where d.internalName = ?1"),
-        @NamedQuery(name = "Database.findAllOnlyIds", query = "select d.id from Database d order by d.created desc"),
-        @NamedQuery(name = "Database.findReadAccess", query = "select distinct d from Database d join DatabaseAccess a on a.hdbid = d.id and a.huserid = ?1"),
-        @NamedQuery(name = "Database.findWriteAccess", query = "select distinct d from Database d join DatabaseAccess a on a.hdbid = d.id and a.huserid = ?1 where a.type = 'WRITE_OWN' or a.type = 'WRITE_ALL'"),
-        @NamedQuery(name = "Database.findConfigureAccess", query = "select distinct d from Database d where d.ownedBy = ?1"),
-        @NamedQuery(name = "Database.findPublicOrMine", query = "select distinct d from Database d where d.id = ?1 and (d.isPublic = true or d.ownedBy = ?2)"),
-        @NamedQuery(name = "Database.findPublic", query = "select distinct d from Database d where d.isPublic = true and d.id = ?1"),
+        @NamedQuery(name = "Database.findAllDesc", query = "select distinct d from Database d order by d.id desc"),
+        @NamedQuery(name = "Database.findAllAtLestReadAccessDesc", query = "select distinct d from Database d where exists(select a.hdbid from DatabaseAccess a where a.huserid = ?1 and a.hdbid = d.id) order by d.id desc"),
+        @NamedQuery(name = "Database.findAllPublicOrSchemaPublicDesc", query = "select distinct d from Database d where d.isPublic = true or d.isSchemaPublic = true order by d.id desc"),
+        @NamedQuery(name = "Database.findAllPublicOrSchemaPublicOrReadAccessDesc", query = "select distinct d from Database d where d.isPublic = true or d.isSchemaPublic = true or exists(select a.hdbid from DatabaseAccess a where a.huserid = ?1 and a.hdbid = d.id) order by d.id desc"),
+        @NamedQuery(name = "Database.findAllPublicOrSchemaPublicOrReadAccessByInternalNameDesc", query = "select distinct d from Database d where (d.isPublic = true or d.isSchemaPublic = true) and d.internalName = ?2 or exists(select a.hdbid from DatabaseAccess a where a.huserid = ?1 and a.hdbid = d.id) order by d.id desc"),
+        @NamedQuery(name = "Database.findAllPublicOrSchemaPublicByInternalNameDesc", query = "select distinct d from Database d where (d.isPublic = true or d.isSchemaPublic = true) and d.internalName = ?1 order by d.id desc"),
 })
 public class Database implements Serializable {
 
diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/DatabaseAccess.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/DatabaseAccess.java
index 079aac84c7d191f71d29657c6837a6ae9f545bab..6a2622be3e7137c97b68ab9ad91e24edd4fae293 100644
--- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/DatabaseAccess.java
+++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/DatabaseAccess.java
@@ -33,6 +33,7 @@ public class DatabaseAccess {
     private UUID huserid;
 
     @ToString.Exclude
+    @EqualsAndHashCode.Exclude
     @org.springframework.data.annotation.Transient
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumns({
diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java
index c732864969c23bf45e4c616bd0c154703615d286..fd87852c6ecce0e2e614a9c7dd05a5e41cfe2e16 100644
--- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java
+++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java
@@ -7,7 +7,6 @@ import lombok.extern.log4j.Log4j2;
 import org.hibernate.annotations.JdbcTypeCode;
 import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
-import java.security.Principal;
 import java.util.List;
 import java.util.UUID;
 
@@ -21,6 +20,9 @@ import java.util.UUID;
 @EqualsAndHashCode
 @EntityListeners(AuditingEntityListener.class)
 @Table(name = "mdb_users")
+@NamedQueries({
+        @NamedQuery(name = "User.findAllInternal", query = "select distinct u from User u where u.isInternal = true")
+})
 public class User {
 
     @Id
@@ -50,6 +52,7 @@ public class User {
     private String language;
 
     @OneToMany(fetch = FetchType.LAZY)
+    @EqualsAndHashCode.Exclude
     @JoinColumns({
             @JoinColumn(name = "user_id", referencedColumnName = "ID", insertable = false, updatable = false)
     })
@@ -61,4 +64,7 @@ public class User {
     @Column(name = "mariadb_password", nullable = false)
     private String mariadbPassword;
 
+    @Column(name = "is_internal", nullable = false, updatable = false)
+    private Boolean isInternal;
+
 }
diff --git a/dbrepo-metadata-service/oai/pom.xml b/dbrepo-metadata-service/oai/pom.xml
index 32925ee9ff1c042a0edb5a22ebb1929f9ff63390..a3778f036317625e12a6b526a7f1fafadbe237f6 100644
--- a/dbrepo-metadata-service/oai/pom.xml
+++ b/dbrepo-metadata-service/oai/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-metadata-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-oai</artifactId>
     <name>dbrepo-metadata-service-oai</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies/>
 
diff --git a/dbrepo-metadata-service/pom.xml b/dbrepo-metadata-service/pom.xml
index 27dd83121f6b83d311872023b306be181ebd409d..2803d9b5f39ddd69f34151251083c35e8a46b406 100644
--- a/dbrepo-metadata-service/pom.xml
+++ b/dbrepo-metadata-service/pom.xml
@@ -11,7 +11,7 @@
     <groupId>at.tuwien</groupId>
     <artifactId>dbrepo-metadata-service</artifactId>
     <name>dbrepo-metadata-service</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <description>Service that manages the metadata</description>
 
@@ -60,6 +60,8 @@
         <aws-s3.version>2.25.23</aws-s3.version>
         <jackson.version>2.15.2</jackson.version>
         <minio.version>8.5.7</minio.version>
+        <sonar.coverage.jacoco.xmlReportPaths>./report/target/site/jacoco-aggregate/jacoco.xml
+        </sonar.coverage.jacoco.xmlReportPaths>
     </properties>
 
     <dependencies>
diff --git a/dbrepo-metadata-service/report/pom.xml b/dbrepo-metadata-service/report/pom.xml
index f71cbfeb96f6307986b2ee87a5f9ba99f8cfd001..6234a844d2c89c01c7e3d200ce79397ac99eac37 100644
--- a/dbrepo-metadata-service/report/pom.xml
+++ b/dbrepo-metadata-service/report/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <artifactId>dbrepo-metadata-service</artifactId>
         <groupId>at.tuwien</groupId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-report</artifactId>
     <name>dbrepo-metadata-service-report</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
diff --git a/dbrepo-metadata-service/repositories/pom.xml b/dbrepo-metadata-service/repositories/pom.xml
index 480ba479be0f98ca6d7748a25545625cce000ecd..44fde031c181fa559abdbce4b81e59f76c5f4b48 100644
--- a/dbrepo-metadata-service/repositories/pom.xml
+++ b/dbrepo-metadata-service/repositories/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <artifactId>dbrepo-metadata-service</artifactId>
         <groupId>at.tuwien</groupId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-repositories</artifactId>
     <name>dbrepo-metadata-service-repositories</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
index f63c387c9865079aca8eb8a3dedf74dcccdc3bba..c5482f70411433f5df08bd0281a60c75d132bf26 100644
--- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
+++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
@@ -236,8 +236,6 @@ public interface MetadataMapper {
     })
     DataCiteDoiRelatedIdentifier relatedIdentifierToDoiRelatedIdentifier(RelatedIdentifier relatedIdentifier);
 
-    Date instantToDate(Instant data);
-
     @Mappings({
             @Mapping(target = "givenNames", source = "person.name.givenNames.value"),
             @Mapping(target = "familyName", source = "person.name.familyName.value"),
@@ -523,11 +521,17 @@ public interface MetadataMapper {
                 .build();
     }
 
-    default TableDto customTableToTableDto(Table data) {
-        return customTableToTableDto(data, true, true, true);
+    default DatabaseAccess userToWriteAllAccess(Database database, User user) {
+        return DatabaseAccess.builder()
+                .type(AccessType.WRITE_ALL)
+                .hdbid(database.getId())
+                .database(database)
+                .huserid(user.getId())
+                .user(user)
+                .build();
     }
 
-    default TableDto customTableToTableDto(Table data, Boolean broker, Boolean statistic, Boolean schema) {
+    default TableDto customTableToTableDto(Table data) {
         final TableDto table = TableDto.builder()
                 .id(data.getId())
                 .name(data.getName())
@@ -548,18 +552,14 @@ public interface MetadataMapper {
                     .map(this::identifierToIdentifierDto)
                     .toList()));
         }
-        if (broker) {
-            table.setQueueName(data.getQueueName());
-            table.setQueueType("quorum");
-            table.setRoutingKey("dbrepo." + data.getTdbid() + "." + data.getId());
-        }
-        if (statistic) {
-            table.setAvgRowLength(data.getAvgRowLength());
-            table.setMaxDataLength(data.getMaxDataLength());
-            table.setDataLength(data.getDataLength());
-            table.setNumRows(data.getNumRows());
-        }
-        if (schema) {
+        table.setQueueName(data.getQueueName());
+        table.setQueueType("quorum");
+        table.setRoutingKey("dbrepo." + data.getTdbid() + "." + data.getId());
+        table.setAvgRowLength(data.getAvgRowLength());
+        table.setMaxDataLength(data.getMaxDataLength());
+        table.setDataLength(data.getDataLength());
+        table.setNumRows(data.getNumRows());
+        if (table.getConstraints() != null) {
             table.getConstraints()
                     .getPrimaryKey()
                     .forEach(pk -> {
@@ -594,12 +594,12 @@ public interface MetadataMapper {
             if (data.getConstraints().getChecks() == null || data.getConstraints().getChecks().isEmpty()) {
                 table.getConstraints().setChecks(new LinkedHashSet<>());
             }
-            if (data.getColumns() != null) {
-                table.setColumns(new LinkedList<>(data.getColumns()
-                        .stream()
-                        .map(this::tableColumnToColumnDto)
-                        .toList()));
-            }
+        }
+        if (data.getColumns() != null) {
+            table.setColumns(new LinkedList<>(data.getColumns()
+                    .stream()
+                    .map(this::tableColumnToColumnDto)
+                    .toList()));
         }
         return table;
     }
@@ -719,10 +719,7 @@ public interface MetadataMapper {
     @Mappings({
             @Mapping(target = "tableId", source = "table.id"),
             @Mapping(target = "databaseId", source = "table.database.id"),
-            @Mapping(target = "isPublic", source = "table.isSchemaPublic"),
-            @Mapping(target = "description", source = "description"),
-            @Mapping(target = "table", ignore = true),
-            @Mapping(target = "views", ignore = true)
+            @Mapping(target = "description", source = "description")
     })
     ColumnDto tableColumnToColumnDto(TableColumn data);
 
@@ -784,6 +781,7 @@ public interface MetadataMapper {
             @Mapping(target = "attributes.orcid", source = "orcid"),
             @Mapping(target = "attributes.affiliation", source = "affiliation"),
             @Mapping(target = "attributes.theme", source = "theme"),
+            @Mapping(target = "attributes.mariadbPassword", source = "mariadbPassword"),
             @Mapping(target = "name", expression = "java(userToFullName(data))"),
             @Mapping(target = "qualifiedName", expression = "java(userToQualifiedName(data))"),
     })
@@ -824,14 +822,10 @@ public interface MetadataMapper {
                 .trim();
     }
 
-    @Mappings({
-            @Mapping(target = "database.views", ignore = true)
-    })
     ViewDto viewToViewDto(View data);
 
     @Mappings({
             @Mapping(target = "databaseId", source = "view.vdbid"),
-            @Mapping(target = "isPublic", source = "view.isPublic")
     })
     ViewColumnDto viewColumnToViewColumnDto(ViewColumn data);
 
@@ -858,18 +852,7 @@ public interface MetadataMapper {
 
     LanguageType languageTypeDtoToLanguageType(LanguageTypeDto data);
 
-    default Boolean onlyIsPublicOrOwner(Boolean isPublic, User caller, User owner, User databaseOwner) {
-        if (isPublic) {
-            return true;
-        }
-        /* private schema */
-        if (caller == null) {
-            return false;
-        }
-        return owner.equals(caller) || databaseOwner.equals(caller);
-    }
-
-    default DatabaseDto customDatabaseToDatabaseDto(Database data, User caller) {
+    default DatabaseDto customDatabaseToDatabaseDto(Database data) {
         if (data == null) {
             return null;
         }
@@ -899,20 +882,19 @@ public interface MetadataMapper {
         if (data.getTables() != null) {
             database.setTables(new LinkedList<>(data.getTables()
                     .stream()
-                    .filter(t -> onlyIsPublicOrOwner(t.getIsSchemaPublic() || t.getIsPublic(), caller, t.getOwner(), t.getDatabase().getOwner()))
                     .map(this::tableToTableBriefDto)
                     .toList()));
         }
         if (data.getViews() != null) {
             database.setViews(new LinkedList<>(data.getViews()
                     .stream()
-                    .filter(v -> onlyIsPublicOrOwner(v.getIsSchemaPublic() || v.getIsPublic(), caller, v.getOwner(), v.getDatabase().getOwner()))
                     .map(this::viewToViewBriefDto)
                     .toList()));
         }
         if (data.getAccesses() != null) {
             database.setAccesses(new LinkedList<>(data.getAccesses()
                     .stream()
+                    .filter(a -> !a.getUser().getIsInternal())
                     .map(this::databaseAccessToDatabaseAccessDto)
                     .toList()));
         }
@@ -925,6 +907,9 @@ public interface MetadataMapper {
         return database;
     }
 
+    @Mappings({
+            @Mapping(target = "ownerId", source = "owner.id")
+    })
     DatabaseBriefDto databaseToDatabaseBriefDto(Database data);
 
     AccessType accessTypeDtoToAccessType(AccessTypeDto data);
diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/DatabaseRepository.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/DatabaseRepository.java
index 3b962bccbb89bad6f6ad5d765860f48df10a0fb0..38a0a8044162cbf3abe9178de26fca4a21e7ee4c 100644
--- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/DatabaseRepository.java
+++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/DatabaseRepository.java
@@ -13,18 +13,14 @@ public interface DatabaseRepository extends JpaRepository<Database, Long> {
 
     List<Database> findAllDesc();
 
-    List<Database> findReadAccess(UUID id);
+    List<Database> findAllPublicOrSchemaPublicDesc();
 
-    List<Database> findWriteAccess(UUID id);
+    List<Database> findAllAtLestReadAccessDesc(UUID id);
 
-    List<Database> findConfigureAccess(UUID id);
+    List<Database> findAllPublicOrSchemaPublicOrReadAccessDesc(UUID id);
 
-    List<Long> findAllOnlyIds();
+    List<Database> findAllPublicOrSchemaPublicOrReadAccessByInternalNameDesc(UUID id, String internalName);
 
-    Optional<Database> findPublicOrMine(Long databaseId, UUID id);
-
-    Optional<Database> findPublic(Long databaseId);
-
-    Optional<Database> findByInternalName(String internalName);
+    List<Database> findAllPublicOrSchemaPublicByInternalNameDesc(String internalName);
 
 }
diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/UserRepository.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/UserRepository.java
index f21596858ac628560634d6449c5ec6aba3e43ce9..7415fb422c03882f7f71786b0edf756c7bd58159 100644
--- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/UserRepository.java
+++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/repository/UserRepository.java
@@ -4,6 +4,7 @@ import at.tuwien.entities.user.User;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
 
@@ -12,6 +13,8 @@ public interface UserRepository extends JpaRepository<User, UUID> {
 
     Optional<User> findByUsername(String username);
 
+    List<User> findAllInternal();
+
     boolean existsByUsername(String username);
 
     boolean existsByEmail(String email);
diff --git a/dbrepo-metadata-service/rest-service/pom.xml b/dbrepo-metadata-service/rest-service/pom.xml
index 67b149976cd1bb218d2e995b89e6c3d8e81ced3a..233d2ac4658612ad06bccc5140bad70c08265ec5 100644
--- a/dbrepo-metadata-service/rest-service/pom.xml
+++ b/dbrepo-metadata-service/rest-service/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <artifactId>dbrepo-metadata-service</artifactId>
         <groupId>at.tuwien</groupId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-rest-service</artifactId>
     <name>dbrepo-metadata-service-rest</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/utils/UserUtil.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java
similarity index 54%
rename from dbrepo-metadata-service/repositories/src/main/java/at/tuwien/utils/UserUtil.java
rename to dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java
index 4e517625ed8bc7d700c1815a8437675d17534d85..4779a6428e33bde6be581921a9d15bd81a25c5b6 100644
--- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/utils/UserUtil.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java
@@ -1,14 +1,17 @@
-package at.tuwien.utils;
+package at.tuwien.endpoints;
 
 import at.tuwien.api.user.UserDetailsDto;
+import at.tuwien.exception.UserNotFoundException;
+import at.tuwien.service.UserService;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.User;
 
 import java.security.Principal;
 import java.util.UUID;
 
-public class UserUtil {
+public abstract class AbstractEndpoint {
 
-    public static boolean hasRole(Principal principal, String role) {
+    public boolean hasRole(Principal principal, String role) {
         if (principal == null || role == null) {
             return false;
         }
@@ -18,7 +21,7 @@ public class UserUtil {
                 .anyMatch(a -> a.getAuthority().equals(role));
     }
 
-    public static boolean isSystem(Principal principal) {
+    public boolean isSystem(Principal principal) {
         if (principal == null) {
             return false;
         }
@@ -28,16 +31,18 @@ public class UserUtil {
                 .anyMatch(a -> a.getAuthority().equals("system"));
     }
 
-    public static UUID getId(Principal principal) {
+    public UUID getId(Principal principal) {
         if (principal == null) {
             return null;
         }
         final Authentication authentication = (Authentication) principal;
-        final UserDetailsDto user = (UserDetailsDto) authentication.getPrincipal();
-        if (user.getId() == null) {
-            return null;
+        if (authentication.getPrincipal() instanceof UserDetailsDto user) {
+            if (user.getId() == null) {
+                throw new IllegalArgumentException("Principal has no id");
+            }
+            return UUID.fromString(user.getId());
         }
-        return UUID.fromString(user.getId());
+        throw new IllegalArgumentException("Unknown principal instance: " + authentication.getPrincipal().getClass());
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
index 99e4d2e6b11d58fa31bd58effbd849a9696c16d1..f184ffc3372ac10e35f2d6686b0c79e2ecaca818 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
@@ -11,7 +11,6 @@ import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.AccessService;
 import at.tuwien.service.DatabaseService;
 import at.tuwien.service.UserService;
-import at.tuwien.utils.UserUtil;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.media.Content;
@@ -35,7 +34,7 @@ import java.util.UUID;
 @RestController
 @CrossOrigin(origins = "*")
 @RequestMapping(path = "/api/database/{databaseId}/access")
-public class AccessEndpoint {
+public class AccessEndpoint extends AbstractEndpoint {
 
     private final UserService userService;
     private final AccessService accessService;
@@ -99,8 +98,7 @@ public class AccessEndpoint {
         log.debug("endpoint give access to database, databaseId={}, userId={}, access.type={}", databaseId, userId,
                 data.getType());
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().getId().equals(caller.getId())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to create access: not owner");
             throw new NotAllowedException("Failed to create access: not owner");
         }
@@ -162,12 +160,19 @@ public class AccessEndpoint {
         log.debug("endpoint modify database access, databaseId={}, userId={}, access.type={}, principal.name={}",
                 databaseId, userId, data.getType(), principal.getName());
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().getId().equals(caller.getId())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to update access: not owner");
             throw new NotAllowedException("Failed to update access: not owner");
         }
+        if (database.getOwner().getId().equals(userId)) {
+            log.error("Failed to update access: the owner must have write-all access");
+            throw new NotAllowedException("Failed to update access: the owner must have write-all access");
+        }
         final User user = userService.findById(userId);
+        if (user.getIsInternal()) {
+            log.error("Failed to update access: the internal user must have write-all access");
+            throw new NotAllowedException("Failed to update access: the internal user must have write-all access");
+        }
         accessService.find(database, user);
         accessService.update(database, user, data.getType());
         return ResponseEntity.accepted()
@@ -204,9 +209,8 @@ public class AccessEndpoint {
             UserNotFoundException, AccessNotFoundException, NotAllowedException {
         log.debug("endpoint get database access, databaseId={}, userId={}, principal.name={}", databaseId, userId,
                 principal.getName());
-        final User caller = userService.findByUsername(principal.getName());
-        if (!userId.equals(caller.getId())) {
-            if (!UserUtil.hasRole(principal, "check-foreign-database-access")) {
+        if (!userId.equals(getId(principal))) {
+            if (!hasRole(principal, "check-foreign-database-access")) {
                 log.error("Failed to find access: foreign user");
                 throw new NotAllowedException("Failed to find access: foreign user");
             }
@@ -261,12 +265,19 @@ public class AccessEndpoint {
             SearchServiceException, SearchServiceConnectionException {
         log.debug("endpoint revoke database access, databaseId={}, userId={}", databaseId, userId);
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().getId().equals(caller.getId())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to revoke access: not owner");
             throw new NotAllowedException("Failed to revoke access: not owner");
         }
+        if (database.getOwner().getId().equals(userId)) {
+            log.error("Failed to revoke access: the owner must have write-all access");
+            throw new NotAllowedException("Failed to revoke access: the owner must have write-all access");
+        }
         final User user = userService.findById(userId);
+        if (user.getIsInternal()) {
+            log.error("Failed to revoke access: the internal user must have write-all access");
+            throw new NotAllowedException("Failed to revoke access: the internal user must have write-all access");
+        }
         accessService.find(database, user);
         accessService.delete(database, user);
         return ResponseEntity.accepted()
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java
index 44a592658ea3f8a512a07cbfe261b39192e3b1e4..acf6a31ca2a583a91ba9bf9e1e4e3c544342eb65 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java
@@ -14,7 +14,10 @@ import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
 
@@ -22,7 +25,7 @@ import java.util.List;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/concept")
-public class ConceptEndpoint {
+public class ConceptEndpoint extends AbstractEndpoint {
 
     private final ConceptService conceptService;
     private final MetadataMapper metadataMapper;
@@ -47,13 +50,11 @@ public class ConceptEndpoint {
     })
     public ResponseEntity<List<ConceptDto>> findAll() {
         log.debug("endpoint list concepts");
-        final List<ConceptDto> dtos = conceptService.findAll()
-                .stream()
-                .map(metadataMapper::tableColumnConceptToConceptDto)
-                .toList();
-        log.trace("Find all concepts resulted in dtos {}", dtos);
         return ResponseEntity.ok()
-                .body(dtos);
+                .body(conceptService.findAll()
+                        .stream()
+                        .map(metadataMapper::tableColumnConceptToConceptDto)
+                        .toList());
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
index 7995b79bf62117f2f360375af08eb93e1d80eee5..3c506cfd0a910d10bba6401eebe5467d1c97e738 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
@@ -10,7 +10,6 @@ import at.tuwien.exception.ContainerNotFoundException;
 import at.tuwien.exception.ImageNotFoundException;
 import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.ContainerService;
-import at.tuwien.utils.UserUtil;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.media.ArraySchema;
@@ -40,7 +39,7 @@ import java.util.stream.Collectors;
 @CrossOrigin(origins = "*")
 @ControllerAdvice
 @RequestMapping(path = "/api/container")
-public class ContainerEndpoint {
+public class ContainerEndpoint extends AbstractEndpoint {
 
     private final MetadataMapper metadataMapper;
     private final ContainerService containerService;
@@ -65,13 +64,11 @@ public class ContainerEndpoint {
     })
     public ResponseEntity<List<ContainerBriefDto>> findAll(@RequestParam(required = false) Integer limit) {
         log.debug("endpoint find all containers, limit={}", limit);
-        final List<ContainerBriefDto> dtos = containerService.getAll(limit)
-                .stream()
-                .map(metadataMapper::containerToContainerBriefDto)
-                .collect(Collectors.toList());
-        log.debug("find all containers resulted in {} container(s)", dtos.size());
         return ResponseEntity.ok()
-                .body(dtos);
+                .body(containerService.getAll(limit)
+                        .stream()
+                        .map(metadataMapper::containerToContainerBriefDto)
+                        .collect(Collectors.toList()));
     }
 
     @PostMapping
@@ -111,11 +108,8 @@ public class ContainerEndpoint {
     public ResponseEntity<ContainerDto> create(@Valid @RequestBody ContainerCreateDto data)
             throws ImageNotFoundException, ContainerAlreadyExistsException {
         log.debug("endpoint create container, data={}", data);
-        final Container container = containerService.create(data);
-        final ContainerDto dto = metadataMapper.containerToContainerDto(container);
-        log.trace("create container resulted in container {}", dto);
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(dto);
+                .body(metadataMapper.containerToContainerDto(containerService.create(data)));
     }
 
     @GetMapping("/{containerId}")
@@ -140,10 +134,8 @@ public class ContainerEndpoint {
             throws ContainerNotFoundException {
         log.debug("endpoint find container, containerId={}", containerId);
         final Container container = containerService.find(containerId);
-        final ContainerDto dto = metadataMapper.containerToContainerDto(container);
-        log.trace("find container resulted in container {}", dto);
         final HttpHeaders headers = new HttpHeaders();
-        if (UserUtil.isSystem(principal)) {
+        if (isSystem(principal)) {
             log.trace("attach privileged credential information");
             headers.set("X-Username", container.getPrivilegedUsername());
             headers.set("X-Password", container.getPrivilegedPassword());
@@ -151,7 +143,7 @@ public class ContainerEndpoint {
         }
         return ResponseEntity.ok()
                 .headers(headers)
-                .body(dto);
+                .body(metadataMapper.containerToContainerDto(container));
     }
 
     @DeleteMapping("/{containerId}")
@@ -175,10 +167,10 @@ public class ContainerEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<Void> delete(@NotNull @PathVariable("containerId") Long containerId) throws ContainerNotFoundException {
+    public ResponseEntity<Void> delete(@NotNull @PathVariable("containerId") Long containerId)
+            throws ContainerNotFoundException {
         log.debug("endpoint delete container, containerId={}", containerId);
-        final Container container = containerService.find(containerId);
-        containerService.remove(container);
+        containerService.remove(containerService.find(containerId));
         return ResponseEntity.accepted()
                 .build();
     }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
index 29c27074d77253c5cecd6e4ea67afb07954e3bed..ad72fb0756f20fac58bc717c0da601f9b5bb1c74 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
@@ -5,11 +5,15 @@ import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.entities.container.Container;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.DatabaseAccess;
+import at.tuwien.entities.database.View;
+import at.tuwien.entities.database.table.Table;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.mapper.MetadataMapper;
-import at.tuwien.service.*;
-import at.tuwien.utils.UserUtil;
+import at.tuwien.service.ContainerService;
+import at.tuwien.service.DatabaseService;
+import at.tuwien.service.StorageService;
+import at.tuwien.service.UserService;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.headers.Header;
@@ -32,29 +36,25 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
 import java.security.Principal;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Optional;
 
 @Log4j2
 @RestController
 @CrossOrigin(origins = "*")
 @RequestMapping(path = "/api/database")
-public class DatabaseEndpoint {
+public class DatabaseEndpoint extends AbstractEndpoint {
 
     private final UserService userService;
-    private final AccessService accessService;
     private final MetadataMapper databaseMapper;
     private final StorageService storageService;
     private final DatabaseService databaseService;
     private final ContainerService containerService;
 
     @Autowired
-    public DatabaseEndpoint(UserService userService, AccessService accessService, MetadataMapper databaseMapper,
-                            StorageService storageService, DatabaseService databaseService,
-                            ContainerService containerService) {
+    public DatabaseEndpoint(UserService userService, MetadataMapper databaseMapper, StorageService storageService,
+                            DatabaseService databaseService, ContainerService containerService) {
         this.userService = userService;
-        this.accessService = accessService;
         this.databaseMapper = databaseMapper;
         this.storageService = storageService;
         this.databaseService = databaseService;
@@ -75,28 +75,35 @@ public class DatabaseEndpoint {
                             mediaType = "application/json",
                             array = @ArraySchema(schema = @Schema(implementation = DatabaseDto.class)))}),
     })
-    public ResponseEntity<List<DatabaseBriefDto>> list(@RequestParam(name = "internal_name", required = false) String internalName) {
+    public ResponseEntity<List<DatabaseBriefDto>> list(@RequestParam(name = "internal_name", required = false) String internalName,
+                                                       Principal principal) {
         log.debug("endpoint list databases, internalName={}", internalName);
-        List<DatabaseBriefDto> dtos = new LinkedList<>();
-        if (internalName != null) {
-            try {
-                dtos = List.of(databaseMapper.databaseToDatabaseBriefDto(databaseService.findByInternalName(internalName)));
-            } catch (DatabaseNotFoundException e) {
-                /* ignore */
+        final List<Database> databases;
+        if (principal != null) {
+            if (internalName != null) {
+                log.debug("filter request to contain only public databases or where user with id {} has at least read access that match internal name {}", getId(principal), internalName);
+                databases = databaseService.findAllPublicOrSchemaPublicOrReadAccessByInternalName(getId(principal), internalName);
+            } else {
+                log.debug("filter request to contain only databases where user with id {} has at least read access", getId(principal));
+                databases = databaseService.findAllPublicOrSchemaPublicOrReadAccess(getId(principal));
             }
         } else {
-            dtos = databaseService.findAll()
-                    .stream()
-                    .map(databaseMapper::databaseToDatabaseBriefDto)
-                    .toList();
+            if (internalName != null) {
+                log.debug("filter request to contain only public databases that match internal name {}", internalName);
+                databases = databaseService.findAllPublicOrSchemaPublicByInternalName(internalName);
+            } else {
+                log.debug("filter request to contain only public databases");
+                databases = databaseService.findAllPublicOrSchemaPublic();
+            }
         }
-        log.trace("list databases resulted in {} database(s)", dtos.size());
         final HttpHeaders headers = new HttpHeaders();
-        headers.set("X-Count", "" + dtos.size());
+        headers.set("X-Count", "" + databases.size());
         headers.set("Access-Control-Expose-Headers", "X-Count");
         return ResponseEntity.status(HttpStatus.OK)
                 .headers(headers)
-                .body(dtos);
+                .body(databases.stream()
+                        .map(databaseMapper::databaseToDatabaseBriefDto)
+                        .toList());
     }
 
     @PostMapping
@@ -150,20 +157,19 @@ public class DatabaseEndpoint {
     })
     public ResponseEntity<DatabaseDto> create(@Valid @RequestBody DatabaseCreateDto data,
                                               @NotNull Principal principal) throws DataServiceException,
-            DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, ContainerNotFoundException,
-            SearchServiceException, SearchServiceConnectionException, ContainerQuotaException {
+            DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException,
+            ContainerNotFoundException, SearchServiceException, SearchServiceConnectionException,
+            ContainerQuotaException {
         log.debug("endpoint create database, data.name={}", data.getName());
         final Container container = containerService.find(data.getCid());
-        final User caller = userService.findByUsername(principal.getName());
         if (container.getDatabases().size() + 1 > container.getQuota()) {
             log.error("Failed to create database: quota of {} exceeded", container.getQuota());
             throw new ContainerQuotaException("Failed to create database: quota of " + container.getQuota() + " exceeded");
         }
-        final User user = userService.findByUsername(principal.getName());
-        final Database database = databaseService.create(container, data, user);
-        final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(database, caller);
+        final User caller = userService.findById(getId(principal));
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(dto);
+                .body(databaseMapper.customDatabaseToDatabaseDto(
+                        databaseService.create(container, data, caller, userService.findAllInternalUsers())));
     }
 
     @PutMapping("/{databaseId}/metadata/table")
@@ -212,13 +218,12 @@ public class DatabaseEndpoint {
             TableNotFoundException {
         log.debug("endpoint refresh database metadata, databaseId={}", databaseId);
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().getId().equals(caller.getId())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to refresh database tables metadata: not owner");
             throw new NotAllowedException("Failed to refresh tables metadata: not owner");
         }
-        final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.updateTableMetadata(database), caller);
-        return ResponseEntity.ok(dto);
+        return ResponseEntity.ok(databaseMapper.customDatabaseToDatabaseDto(
+                databaseService.updateTableMetadata(database)));
     }
 
     @PutMapping("/{databaseId}/metadata/view")
@@ -261,13 +266,12 @@ public class DatabaseEndpoint {
             SearchServiceConnectionException, NotAllowedException, QueryNotFoundException, ViewNotFoundException {
         log.debug("endpoint refresh database metadata, databaseId={}, principal.name={}", databaseId, principal.getName());
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().getId().equals(caller.getId())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to refresh database views metadata: not owner");
             throw new NotAllowedException("Failed to refresh database views metadata: not owner");
         }
-        final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.updateViewMetadata(database), caller);
-        return ResponseEntity.ok(dto);
+        return ResponseEntity.ok(databaseMapper.customDatabaseToDatabaseDto(
+                databaseService.updateViewMetadata(database)));
     }
 
     @PutMapping("/{databaseId}/visibility")
@@ -315,14 +319,13 @@ public class DatabaseEndpoint {
             NotAllowedException, SearchServiceException, SearchServiceConnectionException, UserNotFoundException {
         log.debug("endpoint modify database visibility, databaseId={}, data={}", databaseId, data);
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().equals(caller)) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to modify database visibility: not owner");
             throw new NotAllowedException("Failed to modify database visibility: not owner");
         }
-        final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.modifyVisibility(database, data), caller);
         return ResponseEntity.accepted()
-                .body(dto);
+                .body(databaseMapper.customDatabaseToDatabaseDto(
+                        databaseService.modifyVisibility(database, data)));
     }
 
     @PutMapping("/{databaseId}/owner")
@@ -371,15 +374,14 @@ public class DatabaseEndpoint {
             SearchServiceException, SearchServiceConnectionException {
         log.debug("endpoint transfer database, databaseId={}, transferDto.id={}", databaseId, data.getId());
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
         final User newOwner = userService.findById(data.getId());
-        if (!database.getOwner().equals(caller)) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to transfer database: not owner");
             throw new NotAllowedException("Failed to transfer database: not owner");
         }
-        final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.modifyOwner(database, newOwner), caller);
         return ResponseEntity.accepted()
-                .body(dto);
+                .body(databaseMapper.customDatabaseToDatabaseDto(
+                        databaseService.modifyOwner(database, newOwner)));
     }
 
     @PutMapping("/{databaseId}/image")
@@ -395,13 +397,13 @@ public class DatabaseEndpoint {
                     content = {@Content(
                             mediaType = "application/json",
                             schema = @Schema(implementation = DatabaseDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Database or user could not be found",
+            @ApiResponse(responseCode = "403",
+                    description = "Modify of image is not permitted",
                     content = {@Content(
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "403",
-                    description = "Modify of image is not permitted",
+            @ApiResponse(responseCode = "404",
+                    description = "Database could not be found",
                     content = {@Content(
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
@@ -424,23 +426,21 @@ public class DatabaseEndpoint {
     public ResponseEntity<DatabaseDto> modifyImage(@NotNull @PathVariable("databaseId") Long databaseId,
                                                    @Valid @RequestBody DatabaseModifyImageDto data,
                                                    @NotNull Principal principal) throws NotAllowedException,
-            DatabaseNotFoundException, UserNotFoundException, SearchServiceException, SearchServiceConnectionException,
+            DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException,
             StorageUnavailableException, StorageNotFoundException {
         log.debug("endpoint modify database image, databaseId={}, data.key={}", databaseId, data.getKey());
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().getId().equals(caller.getId())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to update database image: not owner");
             throw new NotAllowedException("Failed to update database image: not owner");
         }
-        final DatabaseDto dto;
         byte[] image = null;
         if (data.getKey() != null) {
             image = storageService.getBytes(data.getKey());
         }
-        dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.modifyImage(database, image), caller);
         return ResponseEntity.accepted()
-                .body(dto);
+                .body(databaseMapper.customDatabaseToDatabaseDto(
+                        databaseService.modifyImage(database, image)));
     }
 
     @GetMapping("/{databaseId}/image")
@@ -461,10 +461,9 @@ public class DatabaseEndpoint {
     public ResponseEntity<byte[]> findPreviewImage(@NotNull @PathVariable("databaseId") Long databaseId)
             throws DatabaseNotFoundException {
         log.debug("endpoint get database preview image, databaseId={}", databaseId);
-        final Database database = databaseService.findById(databaseId);
         return ResponseEntity.ok()
                 .contentType(MediaType.parseMediaType("image/webp"))
-                .body(database.getImage());
+                .body(databaseService.findById(databaseId).getImage());
     }
 
     @GetMapping("/{databaseId}")
@@ -482,6 +481,11 @@ public class DatabaseEndpoint {
                     content = {@Content(
                             mediaType = "application/json",
                             schema = @Schema(implementation = DatabaseDto.class))}),
+            @ApiResponse(responseCode = "403",
+                    description = "Not allowed to view database",
+                    content = {@Content(
+                            mediaType = "application/json",
+                            schema = @Schema(implementation = ApiErrorDto.class))}),
             @ApiResponse(responseCode = "404",
                     description = "Database, user or exchange could not be found",
                     content = {@Content(
@@ -500,27 +504,51 @@ public class DatabaseEndpoint {
     })
     public ResponseEntity<DatabaseDto> findById(@NotNull @PathVariable("databaseId") Long databaseId,
                                                 Principal principal) throws DataServiceException,
-            DataServiceConnectionException, DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException {
+            DataServiceConnectionException, DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException,
+            NotAllowedException {
         log.debug("endpoint find database, databaseId={}", databaseId);
         final Database database = databaseService.findById(databaseId);
-        final User caller;
         if (principal != null) {
-            caller = userService.findByUsername(principal.getName());
+            final Optional<DatabaseAccess> optional = database.getAccesses()
+                    .stream()
+                    .filter(a -> a.getUser().getId().equals(getId(principal)))
+                    .findFirst();
+            if (!database.getIsPublic() && !database.getIsSchemaPublic() && optional.isEmpty() && !isSystem(principal)) {
+                log.error("Failed to find database: not public and no access found");
+                throw new DatabaseNotFoundException("Failed to find database: not public and no access found");
+            }
+            /* reduce metadata */
+            database.setTables(database.getTables()
+                    .stream()
+                    .filter(t -> t.getIsPublic() || t.getIsSchemaPublic() || optional.isPresent())
+                    .toList());
+            database.setViews(database.getViews()
+                    .stream()
+                    .filter(v -> v.getIsPublic() || v.getIsSchemaPublic() || optional.isPresent())
+                    .toList());
+            if (!isSystem(principal) && !database.getOwner().getId().equals(getId(principal))) {
+                log.trace("authenticated user {} is not owner: remove access list", principal.getName());
+                database.setAccesses(List.of());
+            }
         } else {
-            caller = null;
-        }
-        final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(database, caller);
-        if (caller != null && database.getOwner().getId().equals(caller.getId())) {
-            log.debug("current logged-in user is also the owner: additionally load access list");
-            /* only owner sees the access rights */
-            final List<DatabaseAccess> accesses = accessService.list(database);
-            dto.setAccesses(accesses.stream()
-                    .map(databaseMapper::databaseAccessToDatabaseAccessDto)
-                    .collect(Collectors.toList()));
-            log.debug("found {} database accesses", accesses.size());
+            if (!database.getIsPublic() && !database.getIsSchemaPublic()) {
+                log.error("Failed to find database: not public and not authenticated");
+                throw new NotAllowedException("Failed to find database: not public and not authenticated");
+            }
+            /* reduce metadata */
+            database.setTables(database.getTables()
+                    .stream()
+                    .filter(t -> t.getIsPublic() || t.getIsSchemaPublic())
+                    .toList());
+            database.setViews(database.getViews()
+                    .stream()
+                    .filter(v -> v.getIsPublic() || v.getIsSchemaPublic())
+                    .toList());
+            database.setAccesses(List.of());
         }
+        final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(database);
         final HttpHeaders headers = new HttpHeaders();
-        if (UserUtil.isSystem(principal)) {
+        if (isSystem(principal)) {
             headers.set("X-Username", database.getContainer().getPrivilegedUsername());
             headers.set("X-Password", database.getContainer().getPrivilegedPassword());
             headers.set("X-Host", database.getContainer().getHost());
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
index 31fcd6730551277b4eab2a8bdced3e42afbfbcb5..2830f9714a62cb7fea0b0cf616b4bd54eff8d340 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
@@ -12,10 +12,8 @@ import at.tuwien.entities.identifier.IdentifierStatusType;
 import at.tuwien.entities.identifier.IdentifierType;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
-import at.tuwien.gateway.DataServiceGateway;
 import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.*;
-import at.tuwien.utils.UserUtil;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
@@ -29,7 +27,6 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.InputStreamResource;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
@@ -48,11 +45,9 @@ import java.util.regex.Pattern;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/identifier")
-public class IdentifierEndpoint {
+public class IdentifierEndpoint extends AbstractEndpoint {
 
     private final UserService userService;
-    private final ViewService viewService;
-    private final TableService tableService;
     private final AccessService accessService;
     private final EndpointConfig endpointConfig;
     private final MetadataMapper metadataMapper;
@@ -60,17 +55,15 @@ public class IdentifierEndpoint {
     private final MetadataService metadataService;
     private final EndpointValidator endpointValidator;
     private final IdentifierService identifierService;
-    private final DataServiceGateway dataServiceGateway;
+
+    private static final String CREATE_FOREIGN_IDENTIFIER_ROLE = "create-foreign-identifier";
 
     @Autowired
-    public IdentifierEndpoint(UserService userService, ViewService viewService, TableService tableService,
-                              AccessService accessService, EndpointConfig endpointConfig, MetadataMapper metadataMapper,
-                              DatabaseService databaseService, MetadataService metadataService,
-                              EndpointValidator endpointValidator, IdentifierService identifierService,
-                              DataServiceGateway dataServiceGateway) {
+    public IdentifierEndpoint(UserService userService, AccessService accessService, EndpointConfig endpointConfig,
+                              MetadataMapper metadataMapper, DatabaseService databaseService,
+                              MetadataService metadataService, EndpointValidator endpointValidator,
+                              IdentifierService identifierService) {
         this.userService = userService;
-        this.viewService = viewService;
-        this.tableService = tableService;
         this.accessService = accessService;
         this.endpointConfig = endpointConfig;
         this.metadataMapper = metadataMapper;
@@ -78,7 +71,6 @@ public class IdentifierEndpoint {
         this.metadataService = metadataService;
         this.endpointValidator = endpointValidator;
         this.identifierService = identifierService;
-        this.dataServiceGateway = dataServiceGateway;
     }
 
     @GetMapping(produces = {MediaType.APPLICATION_JSON_VALUE, "application/ld+json"})
@@ -119,25 +111,24 @@ public class IdentifierEndpoint {
             return ResponseEntity.ok(List.of());
         }
         log.trace("found persistent identifiers {}", identifiers);
-        switch (accept) {
-            case "application/json":
+        return switch (accept) {
+            case "application/json" -> {
                 log.trace("accept header matches json");
-                final List<IdentifierBriefDto> resource1 = identifiers.stream()
+                yield ResponseEntity.ok(identifiers.stream()
                         .map(metadataMapper::identifierToIdentifierBriefDto)
-                        .toList();
-                log.debug("find identifier resulted in identifiers {}", resource1);
-                return ResponseEntity.ok(resource1);
-            case "application/ld+json":
+                        .toList());
+            }
+            case "application/ld+json" -> {
                 log.trace("accept header matches json-ld");
-                final List<LdDatasetDto> resource2 = identifiers.stream()
+                yield ResponseEntity.ok(identifiers.stream()
                         .map(i -> metadataMapper.identifierToLdDatasetDto(i, endpointConfig.getWebsiteUrl()))
-                        .toList();
-                log.debug("find identifier resulted in identifiers {}", resource2);
-                return ResponseEntity.ok(resource2);
-            default:
+                        .toList());
+            }
+            default -> {
                 log.error("accept header {} is not supported", accept);
                 throw new FormatNotAvailableException("Must provide either application/json or application/ld+json headers");
-        }
+            }
+        };
     }
 
     @GetMapping(value = "/{identifierId}", produces = {MediaType.APPLICATION_JSON_VALUE, "application/ld+json",
@@ -209,30 +200,20 @@ public class IdentifierEndpoint {
         switch (accept) {
             case "application/json":
                 log.trace("accept header matches json");
-                final IdentifierDto resource1 = metadataMapper.identifierToIdentifierDto(identifier);
-                log.debug("find identifier resulted in identifier {}", resource1);
-                return ResponseEntity.ok(resource1);
+                return ResponseEntity.ok(metadataMapper.identifierToIdentifierDto(identifier));
             case "application/ld+json":
                 log.trace("accept header matches json-ld");
-                final LdDatasetDto resource2 = metadataMapper.identifierToLdDatasetDto(identifier, endpointConfig.getWebsiteUrl());
-                log.debug("find identifier resulted in identifier {}", resource2);
-                log.debug("find identifier resulted in identifier {}", resource2);
-                return ResponseEntity.ok(resource2);
+                return ResponseEntity.ok(metadataMapper.identifierToLdDatasetDto(identifier, endpointConfig.getWebsiteUrl()));
             case "text/csv":
                 log.trace("accept header matches csv");
                 if (identifier.getType().equals(IdentifierType.DATABASE)) {
                     log.error("Failed to export dataset: identifier type is database");
                     throw new FormatNotAvailableException("Failed to export dataset: identifier type is database");
                 }
-                final InputStreamResource resource3;
-                resource3 = identifierService.exportResource(identifier);
-                log.debug("find identifier resulted in resource {}", resource3);
-                return ResponseEntity.ok(resource3);
+                return ResponseEntity.ok(identifierService.exportResource(identifier));
             case "text/xml":
                 log.trace("accept header matches xml");
-                final InputStreamResource resource4 = identifierService.exportMetadata(identifier);
-                log.debug("find identifier resulted in resource {}", resource4);
-                return ResponseEntity.ok(resource4);
+                return ResponseEntity.ok(identifierService.exportMetadata(identifier));
         }
         final Pattern regex = Pattern.compile("text\\/bibliography(; ?style=(apa|ieee|bibtex))?");
         final Matcher matcher = regex.matcher(accept);
@@ -348,9 +329,9 @@ public class IdentifierEndpoint {
             throws SearchServiceException, DatabaseNotFoundException, SearchServiceConnectionException,
             MalformedException, DataServiceConnectionException, IdentifierNotFoundException, ExternalServiceException {
         log.debug("endpoint publish identifier, identifierId={}", identifierId);
-        final Identifier identifier = identifierService.find(identifierId);
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(metadataMapper.identifierToIdentifierDto(identifierService.publish(identifier)));
+                .body(metadataMapper.identifierToIdentifierDto(
+                        identifierService.publish(identifierService.find(identifierId))));
     }
 
     @PutMapping("/{identifierId}")
@@ -402,10 +383,10 @@ public class IdentifierEndpoint {
         log.debug("endpoint save identifier, identifierId={}, data.id={}, principal.name={}", identifierId,
                 data.getId(), principal.getName());
         final Database database = databaseService.findById(data.getDatabaseId());
-        final User caller = userService.findByUsername(principal.getName());
+        final User caller = userService.findById(getId(principal));
         final Identifier identifier = identifierService.find(identifierId);
         /* check owner */
-        if (!identifier.getOwner().getId().equals(caller.getId()) && !UserUtil.hasRole(principal, "create-foreign-identifier")) {
+        if (!identifier.getOwner().getId().equals(getId(principal)) && !hasRole(principal, CREATE_FOREIGN_IDENTIFIER_ROLE)) {
             log.error("Failed to save identifier: foreign user");
             throw new NotAllowedException("Failed to save identifier: foreign user");
         }
@@ -419,7 +400,7 @@ public class IdentifierEndpoint {
             final DatabaseAccess access = accessService.find(database, caller);
             log.trace("found access: {}", access);
         } catch (AccessNotFoundException e) {
-            if (!UserUtil.hasRole(principal, "create-foreign-identifier")) {
+            if (!hasRole(principal, CREATE_FOREIGN_IDENTIFIER_ROLE)) {
                 log.error("Failed to save identifier: insufficient role");
                 throw new NotAllowedException("Failed to save identifier: insufficient role");
             }
@@ -501,12 +482,12 @@ public class IdentifierEndpoint {
             IdentifierNotFoundException, ViewNotFoundException, ExternalServiceException {
         log.debug("endpoint create identifier, data.databaseId={}", data.getDatabaseId());
         final Database database = databaseService.findById(data.getDatabaseId());
-        final User caller = userService.findByUsername(principal.getName());
+        final User caller = userService.findById(getId(principal));
         /* check access */
         try {
             accessService.find(database, caller);
         } catch (AccessNotFoundException e) {
-            if (!UserUtil.hasRole(principal, "create-foreign-identifier")) {
+            if (!hasRole(principal, CREATE_FOREIGN_IDENTIFIER_ROLE)) {
                 log.error("Failed to create identifier: insufficient role");
                 throw new NotAllowedException("Failed to create identifier: insufficient role");
             }
@@ -532,8 +513,9 @@ public class IdentifierEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<ExternalMetadataDto> retrieve(@NotNull @Valid @RequestParam String url)
+    public ResponseEntity<ExternalMetadataDto> retrieve(@NotNull @Valid @RequestParam("url") String url)
             throws OrcidNotFoundException, RorNotFoundException, DoiNotFoundException, IdentifierNotSupportedException {
+        log.debug("endpoint retrieve identifier, url={}", url);
         return ResponseEntity.ok(metadataService.findByUrl(url));
     }
 
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
index 2a9dd568703c03932d3a2f20a57bb49b19433fbc..2aaad860a8bbb70e4d7023b00587cc0ad70b7e94 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
@@ -37,7 +37,7 @@ import java.util.List;
 @CrossOrigin(origins = "*")
 @ControllerAdvice
 @RequestMapping(path = "/api/image")
-public class ImageEndpoint {
+public class ImageEndpoint extends AbstractEndpoint {
 
     private final MetadataMapper metadataMapper;
     private final ImageServiceImpl imageService;
@@ -97,11 +97,9 @@ public class ImageEndpoint {
                                            @NotNull Principal principal) throws ImageAlreadyExistsException,
             ImageInvalidException {
         log.debug("endpoint create image, data={}, principal.name={}", data, principal.getName());
-        final ContainerImage image = imageService.create(data, principal);
-        final ImageDto dto = metadataMapper.containerImageToImageDto(image);
-        log.trace("create image resulted in image {}", dto);
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(dto);
+                .body(metadataMapper.containerImageToImageDto(
+                        imageService.create(data, principal)));
     }
 
     @GetMapping("/{imageId}")
@@ -122,12 +120,9 @@ public class ImageEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<ImageDto> findById(@NotNull @PathVariable("imageId") Long imageId) throws ImageNotFoundException {
-        log.debug("endpoint find image, id={}", imageId);
-        final ContainerImage image = imageService.find(imageId);
-        final ImageDto dto = metadataMapper.containerImageToImageDto(image);
-        log.trace("find image resulted in image {}", dto);
+        log.debug("endpoint find image, imageId={}", imageId);
         return ResponseEntity.ok()
-                .body(dto);
+                .body(metadataMapper.containerImageToImageDto(imageService.find(imageId)));
     }
 
     @PutMapping("/{imageId}")
@@ -152,13 +147,10 @@ public class ImageEndpoint {
     public ResponseEntity<ImageDto> update(@NotNull @PathVariable("imageId") Long imageId,
                                            @RequestBody @Valid ImageChangeDto changeDto)
             throws ImageNotFoundException {
-        log.debug("endpoint update image, id={}, changeDto={}", imageId, changeDto);
-        ContainerImage image = imageService.find(imageId);
-        image = imageService.update(image, changeDto);
-        final ImageDto dto = metadataMapper.containerImageToImageDto(image);
-        log.trace("update image resulted in image {}", dto);
+        log.debug("endpoint update image, imageId={}, changeDto={}", imageId, changeDto);
         return ResponseEntity.accepted()
-                .body(dto);
+                .body(metadataMapper.containerImageToImageDto(
+                        imageService.update(imageService.find(imageId), changeDto)));
     }
 
     @DeleteMapping("/{imageId}")
@@ -179,9 +171,8 @@ public class ImageEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<Void> delete(@NotNull @PathVariable("imageId") Long imageId) throws ImageNotFoundException {
-        log.debug("endpoint delete image, id={}", imageId);
-        final ContainerImage image = imageService.find(imageId);
-        imageService.delete(image);
+        log.debug("endpoint delete image, imageId={}", imageId);
+        imageService.delete(imageService.find(imageId));
         return ResponseEntity.accepted()
                 .build();
     }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java
index 75998b03a5c239661a9f7b3022263a00cd0bff4d..18d5e93ca26a824f1b445e39fb82213b0f7fe649 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java
@@ -21,7 +21,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
-import java.util.stream.Collectors;
 
 @Log4j2
 @RestController
@@ -52,12 +51,11 @@ public class LicenseEndpoint {
     })
     public ResponseEntity<List<LicenseDto>> list() {
         log.debug("endpoint list licenses");
-        final List<LicenseDto> licenses = licenseService.findAll()
-                .stream()
-                .map(metadataMapper::licenseToLicenseDto)
-                .toList();
         return ResponseEntity.status(HttpStatus.OK)
-                .body(licenses);
+                .body(licenseService.findAll()
+                        .stream()
+                        .map(metadataMapper::licenseToLicenseDto)
+                        .toList());
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java
index 0a1cddf5dfddbb4559cc4b608952da94b58cb901..0aeeac9401c796b82060d363a5e1ed01e6ac0e61 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java
@@ -56,20 +56,15 @@ public class MessageEndpoint {
     })
     public ResponseEntity<List<BannerMessageDto>> list(@RequestParam(required = false) Boolean active) {
         log.debug("endpoint list messages, active={}", active);
-        List<BannerMessageDto> dtos;
+        final List<BannerMessage> messages;
         if (active != null && active) {
-            dtos = bannerMessageService.getActive()
-                    .stream()
-                    .map(metadataMapper::bannerMessageToBannerMessageDto)
-                    .toList();
+            messages = bannerMessageService.getActive();
         } else {
-            dtos = bannerMessageService.findAll()
-                    .stream()
-                    .map(metadataMapper::bannerMessageToBannerMessageDto)
-                    .toList();
+            messages = bannerMessageService.findAll();
         }
-        log.info("List messages resulted in {} message(s)", dtos.size());
-        return ResponseEntity.ok(dtos);
+        return ResponseEntity.ok(messages.stream()
+                .map(metadataMapper::bannerMessageToBannerMessageDto)
+                .toList());
     }
 
     @GetMapping("/message/{messageId}")
@@ -91,8 +86,8 @@ public class MessageEndpoint {
     public ResponseEntity<BannerMessageDto> find(@NotNull @PathVariable("messageId") Long messageId)
             throws MessageNotFoundException {
         log.debug("endpoint find one maintenance message, messageId={}", messageId);
-        final BannerMessageDto dto = metadataMapper.bannerMessageToBannerMessageDto(bannerMessageService.find(messageId));
-        return ResponseEntity.ok(dto);
+        return ResponseEntity.ok(metadataMapper.bannerMessageToBannerMessageDto(
+                bannerMessageService.find(messageId)));
     }
 
     @PostMapping
@@ -110,10 +105,9 @@ public class MessageEndpoint {
     })
     public ResponseEntity<BannerMessageDto> create(@Valid @RequestBody BannerMessageCreateDto data) {
         log.debug("endpoint create maintenance message, data={}", data);
-        final BannerMessageDto dto = metadataMapper.bannerMessageToBannerMessageDto(bannerMessageService.create(data));
-        log.trace("create maintenance message results in dto {}", dto);
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(dto);
+                .body(metadataMapper.bannerMessageToBannerMessageDto(
+                        bannerMessageService.create(data)));
     }
 
     @PutMapping("/{messageId}")
@@ -139,10 +133,9 @@ public class MessageEndpoint {
             throws MessageNotFoundException {
         log.debug("endpoint update maintenance message, messageId={}, data={}", messageId, data);
         final BannerMessage message = bannerMessageService.find(messageId);
-        final BannerMessageDto dto = metadataMapper.bannerMessageToBannerMessageDto(bannerMessageService.update(message, data));
-        log.trace("update maintenance message results in dto {}", dto);
-        return ResponseEntity.status(HttpStatus.ACCEPTED)
-                .body(dto);
+        return ResponseEntity.accepted()
+                .body(metadataMapper.bannerMessageToBannerMessageDto(
+                        bannerMessageService.update(message, data)));
     }
 
     @DeleteMapping("/{messageId}")
@@ -166,7 +159,7 @@ public class MessageEndpoint {
         log.debug("endpoint delete maintenance message, messageId={}", messageId);
         final BannerMessage message = bannerMessageService.find(messageId);
         bannerMessageService.delete(message);
-        return ResponseEntity.status(HttpStatus.ACCEPTED)
+        return ResponseEntity.accepted()
                 .build();
     }
 
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java
index fbfd40e6289ef20d20ae2ade97b08b9e47f96d96..8a36471816ee9a4d2a89e3fee2edcde59f7361ae 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java
@@ -29,7 +29,7 @@ import java.util.List;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/oai")
-public class MetadataEndpoint {
+public class MetadataEndpoint extends AbstractEndpoint {
 
     private final MetadataService metadataService;
 
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
index cd17c7ac0b15d014769683dadc660a426986cd9e..7bc6bc037e75501bfd7de5006150550a7a42366b 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
@@ -3,9 +3,11 @@ package at.tuwien.endpoints;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.api.semantics.*;
 import at.tuwien.entities.semantics.Ontology;
-import at.tuwien.exception.*;
+import at.tuwien.exception.FilterBadRequestException;
+import at.tuwien.exception.MalformedException;
+import at.tuwien.exception.OntologyNotFoundException;
+import at.tuwien.exception.UriMalformedException;
 import at.tuwien.mapper.MetadataMapper;
-import at.tuwien.mapper.SparqlMapper;
 import at.tuwien.service.EntityService;
 import at.tuwien.service.OntologyService;
 import io.micrometer.observation.annotation.Observed;
@@ -32,7 +34,7 @@ import java.util.List;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/ontology")
-public class OntologyEndpoint {
+public class OntologyEndpoint extends AbstractEndpoint {
 
     private final EntityService entityService;
     private final MetadataMapper metadataMapper;
@@ -59,11 +61,10 @@ public class OntologyEndpoint {
     })
     public ResponseEntity<List<OntologyBriefDto>> findAll() {
         log.debug("endpoint find all ontologies");
-        final List<OntologyBriefDto> dtos = ontologyService.findAll()
+        return ResponseEntity.ok(ontologyService.findAll()
                 .stream()
                 .map(metadataMapper::ontologyToOntologyBriefDto)
-                .toList();
-        return ResponseEntity.ok(dtos);
+                .toList());
     }
 
     @GetMapping("/{ontologyId}")
@@ -85,8 +86,7 @@ public class OntologyEndpoint {
     public ResponseEntity<OntologyDto> find(@NotNull @PathVariable("ontologyId") Long ontologyId)
             throws OntologyNotFoundException {
         log.debug("endpoint find all ontologies, ontologyId={}", ontologyId);
-        final OntologyDto dto = metadataMapper.ontologyToOntologyDto(ontologyService.find(ontologyId));
-        return ResponseEntity.ok(dto);
+        return ResponseEntity.ok(metadataMapper.ontologyToOntologyDto(ontologyService.find(ontologyId)));
     }
 
     @PostMapping
@@ -104,10 +104,9 @@ public class OntologyEndpoint {
     })
     public ResponseEntity<OntologyDto> create(@NotNull @Valid @RequestBody OntologyCreateDto data,
                                               @NotNull Principal principal) {
-        log.debug("endpoint create ontology, data={}", data);
-        final OntologyDto dto = metadataMapper.ontologyToOntologyDto(ontologyService.create(data, principal));
+        log.debug("endpoint create ontology, data={}, principal.name={}", data, principal.getName());
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(dto);
+                .body(metadataMapper.ontologyToOntologyDto(ontologyService.create(data, principal)));
     }
 
     @PutMapping("/{ontologyId}")
@@ -131,11 +130,10 @@ public class OntologyEndpoint {
     public ResponseEntity<OntologyDto> update(@NotNull @PathVariable("ontologyId") Long ontologyId,
                                               @NotNull @Valid @RequestBody OntologyModifyDto data)
             throws OntologyNotFoundException {
-        log.debug("endpoint update ontology, data={}", data);
-        final Ontology ontology = ontologyService.find(ontologyId);
-        final OntologyDto dto = metadataMapper.ontologyToOntologyDto(ontologyService.update(ontology, data));
+        log.debug("endpoint update ontology, ontologyId={}, data={}", ontologyId, data);
         return ResponseEntity.accepted()
-                .body(dto);
+                .body(metadataMapper.ontologyToOntologyDto(
+                        ontologyService.update(ontologyService.find(ontologyId), data)));
     }
 
     @DeleteMapping("/{ontologyId}")
@@ -158,8 +156,7 @@ public class OntologyEndpoint {
     public ResponseEntity<Void> delete(@NotNull @PathVariable("ontologyId") Long ontologyId)
             throws OntologyNotFoundException {
         log.debug("endpoint delete ontology, ontologyId={}", ontologyId);
-        final Ontology ontology = ontologyService.find(ontologyId);
-        ontologyService.delete(ontology);
+        ontologyService.delete(ontologyService.find(ontologyId));
         return ResponseEntity.accepted()
                 .build();
     }
@@ -217,15 +214,12 @@ public class OntologyEndpoint {
             throw new OntologyNotFoundException("Failed to find SPARQL endpoint for ontology with id " + ontology.getId());
         }
         /* get */
-        final List<EntityDto> dtos;
         if (uri != null) {
-            dtos = entityService.findByUri(uri);
             return ResponseEntity.ok()
-                    .body(dtos);
+                    .body(entityService.findByUri(uri));
         }
-        dtos = entityService.findByLabel(ontology, label);
         return ResponseEntity.ok()
-                .body(dtos);
+                .body(entityService.findByLabel(ontology, label));
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index 822581bb991786467123b7adde5e35c1e6cde6e2..9f4542fc023f8aaf336fc2de196d0c36ab93cea4 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -11,12 +11,9 @@ import at.tuwien.api.semantics.EntityDto;
 import at.tuwien.api.semantics.TableColumnEntityDto;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.table.Table;
-import at.tuwien.entities.database.table.columns.TableColumn;
-import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.*;
-import at.tuwien.utils.UserUtil;
 import at.tuwien.validation.EndpointValidator;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
@@ -46,7 +43,7 @@ import java.util.stream.Collectors;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/database/{databaseId}/table")
-public class TableEndpoint {
+public class TableEndpoint extends AbstractEndpoint {
 
     private final UserService userService;
     private final TableService tableService;
@@ -73,7 +70,7 @@ public class TableEndpoint {
     @Transactional(readOnly = true)
     @Observed(name = "dbrepo_tables_findall")
     @Operation(summary = "List tables",
-            description = "Lists all tables known to the metadata database.",
+            description = "Lists all tables known to the metadata database. When a database has a hidden schema (i.e. when `is_schema_public` is `false`), then the user needs to have at least read access and the role `list-tables`.",
             security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200",
@@ -97,14 +94,13 @@ public class TableEndpoint {
             DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException {
         log.debug("endpoint list tables, databaseId={}", databaseId);
         final Database database = databaseService.findById(databaseId);
-        endpointValidator.validateOnlyPrivateAccess(database, principal);
-        endpointValidator.validateOnlyPrivateHasRole(database, principal, "list-tables");
-        final List<TableBriefDto> dto = database.getTables()
+        endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
+        endpointValidator.validateOnlyPrivateSchemaHasRole(database, principal, "list-tables");
+        return ResponseEntity.ok(database.getTables()
                 .stream()
+                .filter(t -> t.getIsPublic() || t.getIsSchemaPublic())
                 .map(metadataMapper::tableToTableBriefDto)
-                .collect(Collectors.toList());
-        log.trace("list tables resulted in tables {}", dto);
-        return ResponseEntity.ok(dto);
+                .collect(Collectors.toList()));
     }
 
     @GetMapping("/{tableId}/suggest")
@@ -154,7 +150,7 @@ public class TableEndpoint {
                 principal);
         final Database database = databaseService.findById(databaseId);
         final Table table = tableService.findById(database, tableId);
-        if (!table.getOwner().getUsername().equals(principal.getName())) {
+        if (!table.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to analyse table semantics: not owner");
             throw new NotAllowedException("Failed to analyse table semantics: not owner");
         }
@@ -207,7 +203,7 @@ public class TableEndpoint {
                 principal.getName());
         final Database database = databaseService.findById(databaseId);
         final Table table = tableService.findById(database, tableId);
-        if (!table.getOwner().getUsername().equals(principal.getName())) {
+        if (!table.getOwner().getId().equals(getId(principal)) && !isSystem(principal)) {
             log.error("Failed to update table statistics: not owner");
             throw new NotAllowedException("Failed to update table statistics: not owner");
         }
@@ -266,19 +262,14 @@ public class TableEndpoint {
         log.debug("endpoint update table, databaseId={}, tableId={}, columnId={}, principal.name={}", databaseId,
                 tableId, columnId, principal.getName());
         final Database database = databaseService.findById(databaseId);
-        final User user = userService.findByUsername(principal.getName());
         final Table table = tableService.findById(database, tableId);
-        if (!UserUtil.hasRole(principal, "modify-foreign-table-column-semantics")) {
-            endpointValidator.validateOnlyAccess(table.getDatabase(), principal, true);
-            endpointValidator.validateOnlyOwnerOrWriteAll(table, user);
+        if (!hasRole(principal, "modify-foreign-table-column-semantics")) {
+            endpointValidator.validateOnlyAccess(database, principal, true);
+            endpointValidator.validateOnlyOwnerOrWriteAll(table, userService.findById(getId(principal)));
         }
-        TableColumn column = tableService.findColumnById(table, columnId);
-        column = tableService.update(column, updateDto);
-        log.info("Updated table semantics of table with id {}", tableId);
-        final ColumnDto columnDto = metadataMapper.tableColumnToColumnDto(column);
-        log.trace("find table data resulted in column {}", columnDto);
         return ResponseEntity.accepted()
-                .body(columnDto);
+                .body(metadataMapper.tableColumnToColumnDto(tableService.update(
+                        tableService.findColumnById(table, columnId), updateDto)));
     }
 
     @GetMapping("/{tableId}/column/{columnId}/suggest")
@@ -317,12 +308,10 @@ public class TableEndpoint {
             throws MalformedException, TableNotFoundException, DatabaseNotFoundException {
         log.debug("endpoint analyse table column semantics, databaseId={}, tableId={}, columnId={}, principal.name={}",
                 databaseId, tableId, columnId, principal.getName());
-        final Database database = databaseService.findById(databaseId);
-        final Table table = tableService.findById(database, tableId);
-        TableColumn column = tableService.findColumnById(table, columnId);
-        final List<TableColumnEntityDto> dtos = entityService.suggestByColumn(column);
         return ResponseEntity.ok()
-                .body(dtos);
+                .body(entityService.suggestByColumn(
+                        tableService.findColumnById(
+                                tableService.findById(databaseService.findById(databaseId), tableId), columnId)));
     }
 
     @PostMapping
@@ -380,11 +369,9 @@ public class TableEndpoint {
         final Database database = databaseService.findById(databaseId);
         endpointValidator.validateOnlyAccess(database, principal, true);
         endpointValidator.validateColumnCreateConstraints(data);
-        final Table table = tableService.createTable(database, data, principal);
-        final TableDto dto = metadataMapper.customTableToTableDto(table);
-        log.info("Created table with id {}", dto.getId());
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(dto);
+                .body(metadataMapper.customTableToTableDto(
+                        tableService.createTable(database, data, principal)));
     }
 
     @PutMapping("/{tableId}")
@@ -436,21 +423,20 @@ public class TableEndpoint {
                 databaseId, data.getIsPublic(), data.getIsSchemaPublic(), principal.getName());
         final Database database = databaseService.findById(databaseId);
         final Table table = tableService.findById(database, tableId);
-        if (!table.getOwner().getUsername().equals(principal.getName())) {
+        if (!table.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to update table: not owner");
             throw new NotAllowedException("Failed to update table: not owner");
         }
-        final TableDto dto = metadataMapper.customTableToTableDto(tableService.updateTable(table, data));
-        log.info("Updated table with id {}", dto.getId());
-        return ResponseEntity.status(HttpStatus.ACCEPTED)
-                .body(dto);
+        return ResponseEntity.accepted()
+                .body(metadataMapper.customTableToTableDto(
+                        tableService.updateTable(table, data)));
     }
 
     @GetMapping("/{tableId}")
     @Transactional(readOnly = true)
     @Observed(name = "dbrepo_tables_find")
     @Operation(summary = "Find table",
-            description = "Finds a table with id. When the `system` role is present, the endpoint responds with additional connection metadata in the header.",
+            description = "Finds a table with id. When a table is hidden (i.e. when `is_public` is `false`), then the user needs to have at least read access and the role `find-table`. When the `system` role is present, the endpoint responds with additional connection metadata in the header.",
             security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200",
@@ -490,29 +476,32 @@ public class TableEndpoint {
     public ResponseEntity<TableDto> findById(@NotNull @PathVariable("databaseId") Long databaseId,
                                              @NotNull @PathVariable("tableId") Long tableId,
                                              Principal principal) throws DataServiceException,
-            DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, QueueNotFoundException {
+            DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, QueueNotFoundException,
+            UserNotFoundException, NotAllowedException, AccessNotFoundException {
         log.debug("endpoint find table, databaseId={}, tableId={}", databaseId, tableId);
         final Database database = databaseService.findById(databaseId);
         final Table table = tableService.findById(database, tableId);
-        boolean hasAccess = UserUtil.isSystem(principal);
         boolean isOwner = false;
-        try {
-            if (principal != null) {
-                final User user = userService.findByUsername(principal.getName());
-                accessService.find(table.getDatabase(), user);
-                hasAccess = true;
-                isOwner = table.getOwner().getId().equals(user.getId());
+        if (principal != null) {
+            isOwner = table.getOwner().getId().equals(getId(principal));
+            if (!table.getIsSchemaPublic()) {
+                try {
+                    accessService.find(table.getDatabase(), userService.findById(getId(principal)));
+                } catch (UserNotFoundException | AccessNotFoundException e) {
+                    if (!isOwner && !isSystem(principal)) {
+                        log.error("Failed to find table with id {}: private and no access permission", table);
+                        throw new NotAllowedException("Failed to find table with id " + tableId + ": private and no access permission");
+                    }
+                }
             }
-        } catch (UserNotFoundException | AccessNotFoundException e) {
-            /* ignore */
         }
-        final boolean includeSchema = UserUtil.isSystem(principal) || isOwner || table.getIsSchemaPublic();
-        log.trace("user has access: {}", hasAccess);
-        log.trace("include schema in mapping: {}", includeSchema);
-        final TableDto dto = metadataMapper.customTableToTableDto(table, hasAccess, table.getDatabase().getIsPublic(),
-                includeSchema);
+        if (!table.getIsSchemaPublic() && !isOwner && !isSystem(principal)) {
+            log.debug("remove schema from table: {}.{}", database.getInternalName(), table.getInternalName());
+            table.setColumns(List.of());
+            table.setConstraints(null);
+        }
         final HttpHeaders headers = new HttpHeaders();
-        if (UserUtil.isSystem(principal)) {
+        if (isSystem(principal)) {
             headers.set("X-Username", table.getDatabase().getContainer().getPrivilegedUsername());
             headers.set("X-Password", table.getDatabase().getContainer().getPrivilegedPassword());
             headers.set("X-Host", table.getDatabase().getContainer().getHost());
@@ -522,9 +511,9 @@ public class TableEndpoint {
             headers.set("X-Table", table.getInternalName());
             headers.set("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-Table");
         }
-        return ResponseEntity.status(HttpStatus.OK)
+        return ResponseEntity.ok()
                 .headers(headers)
-                .body(dto);
+                .body(metadataMapper.customTableToTableDto(table));
     }
 
     @DeleteMapping("/{tableId}")
@@ -573,7 +562,7 @@ public class TableEndpoint {
         final Database database = databaseService.findById(databaseId);
         final Table table = tableService.findById(database, tableId);
         /* roles */
-        if (!table.getOwner().getUsername().equals(principal.getName()) && !UserUtil.hasRole(principal, "delete-foreign-table")) {
+        if (!table.getOwner().getId().equals(getId(principal)) && !hasRole(principal, "delete-foreign-table")) {
             log.error("Failed to delete table: not owned by current user");
             throw new NotAllowedException("Failed to delete table: not owned by current user");
         }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java
index c992f151b5a32bafaf62a85a1a930ffbe7d0bdbe..45aef3645ba93cb1298c6da2e658656f645778ed 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java
@@ -14,7 +14,10 @@ import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
 
@@ -22,7 +25,7 @@ import java.util.List;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/unit")
-public class UnitEndpoint {
+public class UnitEndpoint extends AbstractEndpoint {
 
     private final UnitService unitService;
     private final MetadataMapper metadataMapper;
@@ -47,12 +50,11 @@ public class UnitEndpoint {
     })
     public ResponseEntity<List<UnitDto>> findAll() {
         log.debug("endpoint list units");
-        final List<UnitDto> dtos = unitService.findAll()
-                .stream()
-                .map(metadataMapper::tableColumnUnitToUnitDto)
-                .toList();
         return ResponseEntity.ok()
-                .body(dtos);
+                .body(unitService.findAll()
+                        .stream()
+                        .map(metadataMapper::tableColumnUnitToUnitDto)
+                        .toList());
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java
index 151169c2446f2bc699cb108f56623fbb6a6a3fba..5a349ff3782f1c94a4aea6f4d609f3402437b9c0 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java
@@ -16,7 +16,6 @@ import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.AuthenticationService;
 import at.tuwien.service.DatabaseService;
 import at.tuwien.service.UserService;
-import at.tuwien.utils.UserUtil;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.media.ArraySchema;
@@ -44,7 +43,7 @@ import java.util.UUID;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/user")
-public class UserEndpoint {
+public class UserEndpoint extends AbstractEndpoint {
 
     private final UserService userService;
     private final MetadataMapper userMapper;
@@ -64,7 +63,7 @@ public class UserEndpoint {
     @Transactional(readOnly = true)
     @Observed(name = "dbrepo_users_list")
     @Operation(summary = "List users",
-            description = "Lists users known to the metadata database.")
+            description = "Lists users known to the metadata database. Internal users are omitted from the result list. If the optional query parameter `username` is present, the result list can be filtered by matching this exact username.")
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200",
                     description = "List users",
@@ -77,12 +76,17 @@ public class UserEndpoint {
         if (username == null) {
             return ResponseEntity.ok(userService.findAll()
                     .stream()
+                    .filter(user -> !user.getIsInternal())
                     .map(userMapper::userToUserBriefDto)
                     .toList());
         }
+        log.trace("filter by username: {}", username);
         try {
-            log.trace("filter by username: {}", username);
-            return ResponseEntity.ok(List.of(userMapper.userToUserBriefDto(userService.findByUsername(username))));
+            final User user = userService.findByUsername(username);
+            if (user.getIsInternal()) {
+                return ResponseEntity.ok(List.of());
+            }
+            return ResponseEntity.ok(List.of(userMapper.userToUserBriefDto(user)));
         } catch (UserNotFoundException e) {
             log.trace("filter by username {} failed: return empty list", username);
             return ResponseEntity.ok(List.of());
@@ -141,16 +145,15 @@ public class UserEndpoint {
         log.debug("endpoint create user, data.username={}", data.getUsername());
         userService.validateUsernameNotExists(data.getUsername());
         userService.validateEmailNotExists(data.getEmail());
-        final User user = userService.create(data, authenticationService.create(data).getAttributes().getLdapId()[0]);
-        log.info("Created user with id: {}", user.getId());
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(userMapper.userToUserDto(user));
+                .body(userMapper.userToUserDto(
+                        userService.create(data, authenticationService.create(data).getAttributes().getLdapId()[0])));
     }
 
     @PostMapping("/token")
     @Observed(name = "dbrepo_user_token")
     @Operation(summary = "Create token",
-            description = "Creates a user token via the auth service.")
+            description = "Creates a user token via the Auth Service.")
     @ApiResponses(value = {
             @ApiResponse(responseCode = "202",
                     description = "Obtained user token",
@@ -193,7 +196,6 @@ public class UserEndpoint {
             AccountNotSetupException {
         log.debug("endpoint get token, data.username={}", data.getUsername());
         /* check */
-        final TokenDto token = authenticationService.obtainToken(data);
         try {
             userService.findByUsername(data.getUsername());
         } catch (UserNotFoundException e) {
@@ -213,7 +215,7 @@ public class UserEndpoint {
             log.info("Patched missing user information for user with username: {}", data.getUsername());
         }
         return ResponseEntity.accepted()
-                .body(token);
+                .body(authenticationService.obtainToken(data));
     }
 
     @PutMapping("/token")
@@ -246,9 +248,8 @@ public class UserEndpoint {
             throws AuthServiceConnectionException, CredentialsInvalidException {
         log.debug("endpoint refresh token");
         /* check */
-        final TokenDto token = authenticationService.refreshToken(data.getRefreshToken());
         return ResponseEntity.accepted()
-                .body(token);
+                .body(authenticationService.refreshToken(data.getRefreshToken()));
     }
 
     @GetMapping("/{userId}")
@@ -256,7 +257,7 @@ public class UserEndpoint {
     @PreAuthorize("isAuthenticated()")
     @Observed(name = "dbrepo_user_find")
     @Operation(summary = "Get user",
-            description = "Gets user with id from the metadata database. Requires authentication.",
+            description = "Gets own user information from the metadata database. Requires authentication. Foreign user information can only be obtained if additional role `find-foreign-user` is present. Finding information about internal users results in a 404 error.",
             security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200",
@@ -281,21 +282,21 @@ public class UserEndpoint {
         log.debug("endpoint find a user, userId={}, principal.name={}", userId, principal.getName());
         /* check */
         final User user = userService.findById(userId);
-        if (!user.getUsername().equals(principal.getName())) {
-            if (!UserUtil.hasRole(principal, "find-foreign-user")) {
-                log.error("Failed to find user: foreign user");
-                throw new NotAllowedException("Failed to find user: foreign user");
-            }
+        if (!user.getId().equals(getId(principal)) && !hasRole(principal, "find-foreign-user")) {
+            log.error("Failed to find user: foreign user");
+            throw new NotAllowedException("Failed to find user: foreign user");
+        }
+        if (user.getIsInternal()) {
+            throw new UserNotFoundException("Failed to find user with username: " + user.getUsername());
         }
-        final UserDto dto = userMapper.userToUserDto(user);
         final HttpHeaders headers = new HttpHeaders();
-        if (UserUtil.isSystem(principal)) {
+        if (isSystem(principal)) {
             headers.set("X-Username", user.getUsername());
             headers.set("X-Password", user.getMariadbPassword());
         }
         return ResponseEntity.status(HttpStatus.OK)
                 .headers(headers)
-                .body(dto);
+                .body(userMapper.userToUserDto(user));
     }
 
     @PutMapping("/{userId}")
@@ -333,13 +334,13 @@ public class UserEndpoint {
             UserNotFoundException, DatabaseNotFoundException {
         log.debug("endpoint modify a user, userId={}, data={}", userId, data);
         final User user = userService.findById(userId);
-        if (!user.getUsername().equals(principal.getName())) {
+        if (!user.getId().equals(getId(principal))) {
             log.error("Failed to modify user: not current user {}", user.getId());
             throw new NotAllowedException("Failed to modify user: not current user " + user.getId());
         }
-        final UserDto dto = userMapper.userToUserDto(userService.modify(user, data));
         return ResponseEntity.accepted()
-                .body(dto);
+                .body(userMapper.userToUserDto(
+                        userService.modify(user, data)));
     }
 
     @PutMapping("/{userId}/password")
@@ -383,14 +384,14 @@ public class UserEndpoint {
                                          @NotNull Principal principal) throws NotAllowedException, AuthServiceException,
             AuthServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, DataServiceException,
             DataServiceConnectionException, CredentialsInvalidException {
-        log.debug("endpoint modify a user password, userId={}", userId);
+        log.debug("endpoint modify a user password, userId={}, principal.name={}", userId, principal.getName());
         final User user = userService.findById(userId);
         if (!user.getUsername().equals(principal.getName())) {
             log.error("Failed to modify user password: not current user");
             throw new NotAllowedException("Failed to modify user password: not current user");
         }
         authenticationService.updatePassword(user, data);
-        for (Database database : databaseService.findAllAccess(userId)) {
+        for (Database database : databaseService.findAllAtLestReadAccess(userId)) {
             databaseService.updatePassword(database, user);
         }
         userService.updatePassword(user, data);
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
index 012206289e19f654cb8154668aee2dac598b61b0..8a4a087da20e4578a40801ac670a7ed5a3826521 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
@@ -13,7 +13,6 @@ import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.DatabaseService;
 import at.tuwien.service.UserService;
 import at.tuwien.service.ViewService;
-import at.tuwien.utils.UserUtil;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.headers.Header;
@@ -42,7 +41,7 @@ import java.util.stream.Collectors;
 @CrossOrigin(origins = "*")
 @RestController
 @RequestMapping(path = "/api/database/{databaseId}/view")
-public class ViewEndpoint {
+public class ViewEndpoint extends AbstractEndpoint {
 
     private final UserService userService;
     private final ViewService viewService;
@@ -81,13 +80,16 @@ public class ViewEndpoint {
             DatabaseNotFoundException {
         log.debug("endpoint find all views, databaseId={}", databaseId);
         final Database database = databaseService.findById(databaseId);
-        final User user = principal != null ? userService.findByUsername(principal.getName()) : null;
-        log.trace("find all views for database {}", database);
-        final List<ViewBriefDto> views = viewService.findAll(database, user)
+        final User caller;
+        if (principal != null) {
+            caller = userService.findById(getId(principal));
+        } else {
+            caller = null;
+        }
+        return ResponseEntity.ok(viewService.findAll(database, caller)
                 .stream()
                 .map(metadataMapper::viewToViewBriefDto)
-                .collect(Collectors.toList());
-        return ResponseEntity.ok(views);
+                .collect(Collectors.toList()));
     }
 
     @PostMapping
@@ -141,17 +143,14 @@ public class ViewEndpoint {
             UserNotFoundException, SearchServiceException, SearchServiceConnectionException {
         log.debug("endpoint create view, databaseId={}, data={}", databaseId, data);
         final Database database = databaseService.findById(databaseId);
-        final User caller = userService.findByUsername(principal.getName());
-        if (!database.getOwner().getId().equals(caller.getId())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to create view: not the database owner");
             throw new NotAllowedException("Failed to create view: not the database owner");
         }
         log.trace("create view for database {}", database);
-        final View view;
-        view = viewService.create(database, caller, data);
-        final ViewBriefDto dto = metadataMapper.viewToViewBriefDto(view);
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(dto);
+                .body(metadataMapper.viewToViewBriefDto(
+                        viewService.create(database, userService.findById(getId(principal)), data)));
     }
 
     @GetMapping("/{viewId}")
@@ -193,13 +192,13 @@ public class ViewEndpoint {
         final Database database = databaseService.findById(databaseId);
         final View view = viewService.findById(database, viewId);
         final HttpHeaders headers = new HttpHeaders();
-        if (UserUtil.isSystem(principal)) {
-            headers.set("X-Username", view.getDatabase().getContainer().getPrivilegedUsername());
-            headers.set("X-Password", view.getDatabase().getContainer().getPrivilegedPassword());
-            headers.set("X-Host", view.getDatabase().getContainer().getHost());
-            headers.set("X-Port", "" + view.getDatabase().getContainer().getPort());
-            headers.set("X-Type", view.getDatabase().getContainer().getImage().getJdbcMethod());
-            headers.set("X-Database", view.getDatabase().getInternalName());
+        if (isSystem(principal)) {
+            headers.set("X-Username", database.getContainer().getPrivilegedUsername());
+            headers.set("X-Password", database.getContainer().getPrivilegedPassword());
+            headers.set("X-Host", database.getContainer().getHost());
+            headers.set("X-Port", "" + database.getContainer().getPort());
+            headers.set("X-Type", database.getContainer().getImage().getJdbcMethod());
+            headers.set("X-Database", database.getInternalName());
             headers.set("X-View", view.getInternalName());
             headers.set("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-View");
         }
@@ -253,10 +252,10 @@ public class ViewEndpoint {
                                     @NotNull @PathVariable("viewId") Long viewId,
                                     @NotNull Principal principal) throws NotAllowedException, DataServiceException,
             DataServiceConnectionException, DatabaseNotFoundException, ViewNotFoundException, SearchServiceException,
-            SearchServiceConnectionException {
+            SearchServiceConnectionException, UserNotFoundException {
         log.debug("endpoint delete view, databaseId={}, viewId={}", databaseId, viewId);
         final Database database = databaseService.findById(databaseId);
-        if (!database.getOwner().getUsername().equals(principal.getName())) {
+        if (!database.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to delete view: not the database owner {}", database.getOwner().getId());
             throw new NotAllowedException("Failed to delete view: not the database owner " + database.getOwner().getId());
         }
@@ -307,16 +306,17 @@ public class ViewEndpoint {
                                           @NotNull @Valid @RequestBody ViewUpdateDto data,
                                           @NotNull Principal principal) throws NotAllowedException,
             DataServiceConnectionException, DatabaseNotFoundException, ViewNotFoundException, SearchServiceException,
-            SearchServiceConnectionException {
+            SearchServiceConnectionException, UserNotFoundException {
         log.debug("endpoint update view, databaseId={}, viewId={}", databaseId, viewId);
         final Database database = databaseService.findById(databaseId);
         final View view = viewService.findById(database, viewId);
-        if (!database.getOwner().getUsername().equals(principal.getName()) && !view.getOwner().getUsername().equals(principal.getName())) {
+        if (!database.getOwner().getId().equals(getId(principal)) && !view.getOwner().getId().equals(getId(principal))) {
             log.error("Failed to update view: not the database- or view owner");
             throw new NotAllowedException("Failed to update view: not the database- or view owner");
         }
         return ResponseEntity.accepted()
-                .body(metadataMapper.viewToViewDto(viewService.update(database, view, data)));
+                .body(metadataMapper.viewToViewDto(
+                        viewService.update(database, view, data)));
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
index 70f2e4e420eb104b7e76caaa702ed23850fe9565..1f7c391bd2ef63d49ed1acd5255409a1564af0ca 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
@@ -5,6 +5,7 @@ import at.tuwien.api.database.table.TableCreateDto;
 import at.tuwien.api.database.table.columns.ColumnCreateDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
 import at.tuwien.api.identifier.IdentifierSaveDto;
+import at.tuwien.endpoints.AbstractEndpoint;
 import at.tuwien.entities.database.AccessType;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.DatabaseAccess;
@@ -13,7 +14,6 @@ import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.service.AccessService;
 import at.tuwien.service.UserService;
-import at.tuwien.utils.UserUtil;
 import lombok.extern.log4j.Log4j2;
 import org.apache.commons.validator.GenericValidator;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,7 +27,7 @@ import java.util.Optional;
 
 @Log4j2
 @Component
-public class EndpointValidator {
+public class EndpointValidator extends AbstractEndpoint {
 
     public static final List<ColumnTypeDto> NEED_NOTHING = List.of(ColumnTypeDto.BOOL, ColumnTypeDto.SERIAL);
     public static final List<ColumnTypeDto> NEED_SIZE = List.of(ColumnTypeDto.VARCHAR, ColumnTypeDto.BINARY, ColumnTypeDto.VARBINARY);
@@ -43,7 +43,7 @@ public class EndpointValidator {
         this.accessService = accessService;
     }
 
-    public void validateOnlyPrivateAccess(Database database, Principal principal, boolean writeAccessOnly)
+    public void validateOnlyPrivateDataAccess(Database database, Principal principal, boolean writeAccessOnly)
             throws NotAllowedException, UserNotFoundException, AccessNotFoundException {
         if (database.getIsPublic()) {
             log.trace("database with id {} is public: no access needed", database.getId());
@@ -52,9 +52,23 @@ public class EndpointValidator {
         validateOnlyAccess(database, principal, writeAccessOnly);
     }
 
-    public void validateOnlyPrivateAccess(Database database, Principal principal) throws NotAllowedException,
+    public void validateOnlyPrivateSchemaAccess(Database database, Principal principal, boolean writeAccessOnly)
+            throws NotAllowedException, UserNotFoundException, AccessNotFoundException {
+        if (database.getIsSchemaPublic()) {
+            log.trace("database schema with id {} is public: no access needed", database.getId());
+            return;
+        }
+        validateOnlyAccess(database, principal, writeAccessOnly);
+    }
+
+    public void validateOnlyPrivateDataAccess(Database database, Principal principal) throws NotAllowedException,
             UserNotFoundException, AccessNotFoundException {
-        validateOnlyPrivateAccess(database, principal, false);
+        validateOnlyPrivateDataAccess(database, principal, false);
+    }
+
+    public void validateOnlyPrivateSchemaAccess(Database database, Principal principal) throws NotAllowedException,
+            UserNotFoundException, AccessNotFoundException {
+        validateOnlyPrivateSchemaAccess(database, principal, false);
     }
 
     public void validateOnlyAccess(Database database, Principal principal, boolean writeAccessOnly)
@@ -62,8 +76,10 @@ public class EndpointValidator {
         if (principal == null) {
             throw new NotAllowedException("No principal provided");
         }
-        final User user = userService.findByUsername(principal.getName());
-        final DatabaseAccess access = accessService.find(database, user);
+        if (isSystem(principal)) {
+            return;
+        }
+        final DatabaseAccess access = accessService.find(database, userService.findById(getId(principal)));
         log.trace("found access: {}", access);
         if (writeAccessOnly && !(access.getType().equals(AccessType.WRITE_OWN) || access.getType().equals(AccessType.WRITE_ALL))) {
             log.error("Access not allowed: no write access");
@@ -167,7 +183,7 @@ public class EndpointValidator {
     }
 
     public boolean validateOnlyMineOrWriteAccessOrHasRole(User owner, Principal principal, DatabaseAccess access, String role) {
-        if (UserUtil.hasRole(principal, role)) {
+        if (hasRole(principal, role)) {
             log.debug("validation passed: role {} present", role);
             return true;
         }
@@ -222,7 +238,7 @@ public class EndpointValidator {
         throw new NotAllowedException("Access not allowed: insufficient access (neither owner nor write-all access)");
     }
 
-    public void validateOnlyPrivateHasRole(Database database, Principal principal, String role)
+    public void validateOnlyPrivateDataHasRole(Database database, Principal principal, String role)
             throws NotAllowedException {
         if (database.getIsPublic()) {
             log.trace("database with id {} is public: no access needed", database.getId());
@@ -234,7 +250,26 @@ public class EndpointValidator {
             throw new NotAllowedException("Access not allowed: no authorization provided");
         }
         log.trace("principal: {}", principal.getName());
-        if (!UserUtil.hasRole(principal, role)) {
+        if (!hasRole(principal, role)) {
+            log.error("Access not allowed: role {} missing", role);
+            throw new NotAllowedException("Access not allowed: role " + role + " missing");
+        }
+        log.trace("principal has role '{}': access granted", role);
+    }
+
+    public void validateOnlyPrivateSchemaHasRole(Database database, Principal principal, String role)
+            throws NotAllowedException {
+        if (database.getIsSchemaPublic()) {
+            log.trace("database with id {} has public schema: no access needed", database.getId());
+            return;
+        }
+        log.trace("database with id {} has private schema", database.getId());
+        if (principal == null) {
+            log.error("Access not allowed: no authorization provided");
+            throw new NotAllowedException("Access not allowed: no authorization provided");
+        }
+        log.trace("principal: {}", principal.getName());
+        if (!hasRole(principal, role)) {
             log.error("Access not allowed: role {} missing", role);
             throw new NotAllowedException("Access not allowed: role " + role + " missing");
         }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java
index b788fd206b962c89644d2f0e6802b1a15282627a..11d64faf8b9562ff42ab2120d5f6fa151710d462 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java
@@ -110,15 +110,12 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(containerService.find(CONTAINER_1_ID))
                 .thenReturn(CONTAINER_1);
-        when(databaseService.create(CONTAINER_1, request, USER_1))
-                .thenReturn(DATABASE_1);
-        doNothing()
-                .when(messageQueueService)
-                .setVirtualHostPermissions(USER_1);
-        when(keycloakGateway.findByUsername(USER_1_USERNAME))
-                .thenReturn(USER_1_KEYCLOAK_DTO);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
+        when(userService.findAllInternalUsers())
+                .thenReturn(List.of(USER_LOCAL));
+        when(databaseService.create(CONTAINER_1, request, USER_1, List.of(USER_LOCAL)))
+                .thenReturn(DATABASE_1);
 
         /* test */
         create_generic(request, USER_1_PRINCIPAL, USER_1);
@@ -136,7 +133,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(containerService.find(CONTAINER_4_ID))
                 .thenReturn(CONTAINER_4);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
 
         /* test */
@@ -174,7 +171,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_2_USERNAME))
+        when(userService.findById(USER_2_ID))
                 .thenReturn(USER_2);
         when(databaseService.updateTableMetadata(any(Database.class)))
                 .thenReturn(DATABASE_1);
@@ -195,7 +192,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
         when(databaseService.updateTableMetadata(any(Database.class)))
                 .thenReturn(DATABASE_1);
@@ -215,7 +212,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
         when(databaseService.updateViewMetadata(any(Database.class)))
                 .thenReturn(DATABASE_1);
@@ -233,7 +230,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_2_USERNAME))
+        when(userService.findById(USER_2_ID))
                 .thenReturn(USER_2);
 
         /* test */
@@ -249,7 +246,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
 
         /* test */
@@ -265,7 +262,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
 
         /* test */
@@ -276,51 +273,68 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void list_anonymous_succeeds() throws DatabaseNotFoundException {
+    public void list_anonymous_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
 
-        /* pre-condition */
-        assertFalse(DATABASE_1_PUBLIC);
+        /* mock */
+        when(databaseService.findAllPublicOrSchemaPublic())
+                .thenReturn(List.of(DATABASE_1));
 
         /* test */
-        list_generic(List.of(DATABASE_1), null);
+        list_generic(null, null, 1);
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRole_succeeds() throws DatabaseNotFoundException {
+    public void list_hasRole_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
 
         /* pre-condition */
         assertTrue(DATABASE_3_PUBLIC);
 
+        /* mock */
+        when(databaseService.findAllPublicOrSchemaPublicOrReadAccess(any(UUID.class)))
+                .thenReturn(List.of(DATABASE_3));
+
         /* test */
-        list_generic(List.of(DATABASE_3), null);
+        list_generic(null, USER_1_PRINCIPAL, 1);
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRoleForeign_succeeds() throws DatabaseNotFoundException {
+    public void list_hasRoleForeign_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
 
         /* pre-condition */
         assertTrue(DATABASE_3_PUBLIC);
 
+        /* mock */
+        when(databaseService.findAllPublicOrSchemaPublicOrReadAccess(USER_1_ID))
+                .thenReturn(List.of(DATABASE_3));
+
         /* test */
-        list_generic(List.of(DATABASE_3), null);
+        list_generic(null, USER_1_PRINCIPAL, 1);
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRoleFilter_succeeds() throws DatabaseNotFoundException {
+    public void list_hasRoleFilter_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
+
+        /* mock */
+        when(databaseService.findAllPublicOrSchemaPublicOrReadAccessByInternalName(USER_1_ID, DATABASE_3_INTERNALNAME))
+                .thenReturn(List.of(DATABASE_3));
 
         /* test */
-        list_generic(List.of(DATABASE_3), DATABASE_3_INTERNALNAME);
+        list_generic(DATABASE_3_INTERNALNAME, USER_1_PRINCIPAL, 1);
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRoleFilterNoResult_succeeds() throws DatabaseNotFoundException {
+    public void list_hasRoleFilterNoResult_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
+
+        /* mock */
+        when(databaseService.findAllPublicOrSchemaPublicOrReadAccessByInternalName(USER_1_ID, "i_do_not_exist"))
+                .thenReturn(List.of());
 
         /* test */
-        list_generic(List.of(), "i_do_not_exist");
+        list_generic("i_do_not_exist", USER_1_PRINCIPAL, 0);
     }
 
     @Test
@@ -348,7 +362,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(keycloakGateway.findByUsername(USER_1_USERNAME))
                 .thenReturn(USER_1_KEYCLOAK_DTO);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
 
         /* test */
@@ -376,7 +390,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
                 .build();
 
         /* mock */
-        when(userService.findByUsername(USER_2_USERNAME))
+        when(userService.findById(USER_2_ID))
                 .thenReturn(USER_2);
 
         /* test */
@@ -408,7 +422,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_3_ID))
                 .thenReturn(DATABASE_3);
-        when(userService.findByUsername(USER_2_USERNAME))
+        when(userService.findById(USER_2_ID))
                 .thenReturn(USER_2);
 
         /* test */
@@ -429,7 +443,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
         when(storageService.getBytes(request.getKey()))
                 .thenReturn(new byte[]{1, 2, 3, 4, 5});
@@ -450,7 +464,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
 
         /* test */
@@ -480,7 +494,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_2_USERNAME))
+        when(userService.findById(USER_2_ID))
                 .thenReturn(USER_2);
         when(userService.findById(USER_4_ID))
                 .thenReturn(USER_4);
@@ -505,7 +519,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1);
         when(keycloakGateway.findByUsername(USER_1_USERNAME))
                 .thenReturn(USER_1_KEYCLOAK_DTO);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
         when(userService.findById(USER_4_ID))
                 .thenReturn(USER_4);
@@ -536,11 +550,12 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void findById_anonymous_succeeds() throws DataServiceException, DataServiceConnectionException,
-            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException {
+    public void findById_anonymous_fails() {
 
         /* test */
-        findById_generic(DATABASE_1_ID, DATABASE_1, null);
+        assertThrows(NotAllowedException.class, () -> {
+            findById_generic(DATABASE_1_ID, DATABASE_1, null);
+        });
     }
 
     @Test
@@ -556,7 +571,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"find-database"})
     public void findById_hasRole_succeeds() throws DataServiceException, DataServiceConnectionException,
-            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException {
+            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException, NotAllowedException {
 
         /* pre-condition */
         assertTrue(DATABASE_3_PUBLIC);
@@ -568,7 +583,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"find-database"})
     public void findById_hasRoleForeign_succeeds() throws DataServiceException, DataServiceConnectionException,
-            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException {
+            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException, NotAllowedException {
 
         /* pre-condition */
         assertTrue(DATABASE_3_PUBLIC);
@@ -580,7 +595,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"find-database"})
     public void findById_ownerSeesAccessRights_succeeds() throws DataServiceException, DataServiceConnectionException,
-            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException {
+            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException, NotAllowedException {
 
         /* mock */
         when(accessService.list(DATABASE_1))
@@ -643,28 +658,15 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
 
-    public void list_generic(List<Database> databases, String internalName) throws DatabaseNotFoundException {
-
-        /* mock */
-        when(databaseService.findAll())
-                .thenReturn(databases);
-        if (internalName != null) {
-            if (!databases.isEmpty()) {
-                when(databaseService.findByInternalName(internalName))
-                        .thenReturn(databases.get(0));
-            } else {
-                doThrow(DatabaseNotFoundException.class)
-                        .when(databaseService)
-                        .findByInternalName(internalName);
-            }
-        }
+    public void list_generic(String internalName, Principal principal, Integer expectedSize)
+            throws DatabaseNotFoundException, UserNotFoundException {
 
         /* test */
-        final ResponseEntity<List<DatabaseBriefDto>> response = databaseEndpoint.list(internalName);
+        final ResponseEntity<List<DatabaseBriefDto>> response = databaseEndpoint.list(internalName, principal);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         assertNotNull(response.getBody());
         final List<DatabaseBriefDto> body = response.getBody();
-        assertEquals(databases.size(), body.size());
+        assertEquals(expectedSize, body.size());
     }
 
     public void create_generic(DatabaseCreateDto data, Principal principal, User user) throws DataServiceException,
@@ -709,7 +711,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     public DatabaseDto findById_generic(Long databaseId, Database database, Principal principal)
             throws DataServiceConnectionException, DatabaseNotFoundException, ExchangeNotFoundException,
-            DataServiceException, UserNotFoundException {
+            DataServiceException, UserNotFoundException, NotAllowedException {
 
         /* mock */
         if (database != null) {
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
index ce560cb646342f627a3e78d09a11f77a59c1223f..74a252c5a63572e569a1e7b8379637e2d34dc03d 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
@@ -1089,7 +1089,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
             throws UserNotFoundException {
 
         /* mock */
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
 
         /* test */
@@ -1163,7 +1163,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
         when(accessService.find(DATABASE_1, USER_1))
                 .thenReturn(DATABASE_1_USER_1_READ_ACCESS);
@@ -1185,7 +1185,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_1_USERNAME))
+        when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
         doThrow(AccessNotFoundException.class)
                 .when(accessService)
@@ -1209,7 +1209,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(DATABASE_1_ID))
                 .thenReturn(DATABASE_1);
-        when(userService.findByUsername(USER_2_USERNAME))
+        when(userService.findById(USER_2_ID))
                 .thenReturn(USER_2);
         doThrow(AccessNotFoundException.class)
                 .when(accessService)
@@ -1270,7 +1270,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         }
         when(identifierService.find(identifier.getId()))
                 .thenReturn(identifier);
-        when(userService.findByUsername(principal.getName()))
+        when(userService.findById(user.getId()))
                 .thenReturn(user);
         when(databaseService.findById(databaseId))
                 .thenReturn(database);
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
index dcc10f61a70ef663f0b4427c60f1acefba8f58ed..ef06d7f37fd01373df24be3c27f95919850b9fae 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
@@ -101,11 +101,21 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void list_publicAnonymous_succeeds() throws NotAllowedException, UserNotFoundException,
+    public void list_publicDataPrivateSchemaAnonymous_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_list(DATABASE_3_ID, DATABASE_3, null, null, null);
+        });
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void list_publicDataPublicSchemaAnonymous_succeeds() throws UserNotFoundException, NotAllowedException,
             DatabaseNotFoundException, AccessNotFoundException {
 
         /* test */
-        generic_list(DATABASE_3_ID, DATABASE_3, null, null, null);
+        generic_list(DATABASE_4_ID, DATABASE_4, null, null, null);
     }
 
     @Test
@@ -132,10 +142,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_4_USERNAME)
-    public void list_publicNoRole_succeeds() throws NotAllowedException, UserNotFoundException, DatabaseNotFoundException, AccessNotFoundException {
+    public void list_publicDataPrivateSchemaNoRole_fails() {
 
         /* test */
-        generic_list(DATABASE_3_ID, DATABASE_3, USER_4_PRINCIPAL, USER_4, null);
+        assertThrows(NotAllowedException.class, () -> {
+            generic_list(DATABASE_3_ID, DATABASE_3, USER_4_PRINCIPAL, USER_4, null);
+        });
     }
 
     @Test
@@ -511,12 +523,24 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void findById_publicAnonymous_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
-            UserNotFoundException {
+    public void findById_publicDatabasePrivateDataPrivateSchemaAnonymous_succeeds() throws UserNotFoundException,
+            TableNotFoundException, NotAllowedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, DataServiceConnectionException {
 
         /* test */
-        generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, null, null, null);
+        final ResponseEntity<TableDto> response = generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, null, null, null);
+        final TableDto body = response.getBody();
+        assertNull(body.getConstraints());
+        assertEquals(List.of(), body.getColumns());
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findById_publicDataPublicSchemaAnonymous_succeeds() throws DataServiceException,
+            DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException,
+            QueueNotFoundException, UserNotFoundException, NotAllowedException {
+
+        /* test */
+        generic_findById(DATABASE_4_ID, DATABASE_4, TABLE_9_ID, TABLE_9, null, null, null);
     }
 
     @Test
@@ -543,7 +567,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_1_USERNAME, authorities = "find-table")
     public void findById_publicHasRole_succeeds() throws DataServiceException, DataServiceConnectionException,
             TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
-            UserNotFoundException {
+            UserNotFoundException, NotAllowedException {
 
         /* test */
         final ResponseEntity<TableDto> response = generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
@@ -556,7 +580,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_4_USERNAME)
     public void findById_publicNoRole_succeeds() throws DataServiceException, DataServiceConnectionException,
             TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
-            UserNotFoundException {
+            UserNotFoundException, NotAllowedException {
 
         /* test */
         generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, null);
@@ -898,12 +922,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void findById_privateAnonymous_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
-            UserNotFoundException {
+    public void findById_privateDatabasePrivateDataPublicSchemaAnonymous_fails() throws UserNotFoundException,
+            TableNotFoundException, NotAllowedException, DataServiceException, DatabaseNotFoundException,
+            AccessNotFoundException, QueueNotFoundException, DataServiceConnectionException {
 
         /* test */
-        generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, null, null, null);
+        generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_2_ID, TABLE_2, null, null, null);
     }
 
     @Test
@@ -930,7 +954,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_1_USERNAME, authorities = "find-table")
     public void findById_privateHasRole_succeeds() throws DataServiceException, DataServiceConnectionException,
             TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
-            UserNotFoundException {
+            UserNotFoundException, NotAllowedException {
         /* test */
         final ResponseEntity<TableDto> response = generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1,
                 USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
@@ -941,9 +965,9 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_4_USERNAME)
-    public void findById_privateNoRole_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
-            UserNotFoundException {
+    public void findById_privateDatabasePrivateDataPrivateSchemaNoRole_succeeds() throws UserNotFoundException,
+            TableNotFoundException, NotAllowedException, DataServiceException, DatabaseNotFoundException,
+            AccessNotFoundException, QueueNotFoundException, DataServiceConnectionException {
 
         /* test */
         generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_4_PRINCIPAL, USER_4, null);
@@ -980,13 +1004,13 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    @WithMockUser(username = USER_2_USERNAME, authorities = {"delete-foreign-table"})
+    @WithMockUser(username = USER_5_USERNAME, authorities = {"delete-foreign-table"})
     public void delete_foreign_succeeds() throws NotAllowedException, DataServiceConnectionException,
             TableNotFoundException, DatabaseNotFoundException, SearchServiceException,
             SearchServiceConnectionException, DataServiceException {
 
         /* test */
-        generic_delete(USER_2_PRINCIPAL, TABLE_1);
+        generic_delete(USER_5_PRINCIPAL, TABLE_1);
     }
 
     @Test
@@ -1132,7 +1156,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* mock */
         if (principal != null) {
-            when(userService.findByUsername(principal.getName()))
+            when(userService.findById(user.getId()))
                     .thenReturn(user);
         }
         if (database != null) {
@@ -1160,7 +1184,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                                                         Table table, Principal principal, User user,
                                                         DatabaseAccess access) throws DataServiceException,
             DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException,
-            QueueNotFoundException, UserNotFoundException {
+            QueueNotFoundException, UserNotFoundException, NotAllowedException {
 
         /* mock */
         if (database != null) {
@@ -1180,7 +1204,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                     .findById(any(Database.class), eq(tableId));
         }
         if (principal != null) {
-            when(userService.findByUsername(principal.getName()))
+            when(userService.findById(user.getId()))
                     .thenReturn(user);
             when(accessService.find(any(Database.class), eq(user)))
                     .thenReturn(access);
@@ -1231,7 +1255,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
         }
         if (principal != null) {
             log.trace("mock user {}", user);
-            when(userService.findByUsername(principal.getName()))
+            when(userService.findById(user.getId()))
                     .thenReturn(user);
         }
         if (access != null) {
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java
index 54cef2ca4b1e08b2feacc94cc00781687a5e8881..be0ea28c496d9c3bd65df14f8a312af5c9003069 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java
@@ -192,7 +192,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
                 .firstname(USER_1_FIRSTNAME)
                 .lastname(USER_1_LASTNAME)
                 .affiliation(USER_1_AFFILIATION)
-                .orcid(USER_1_ORCID)
+                .orcid(USER_1_ORCID_URL)
                 .build();
 
         /* test */
@@ -208,7 +208,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
                 .firstname(USER_1_FIRSTNAME)
                 .lastname(USER_1_LASTNAME)
                 .affiliation(USER_1_AFFILIATION)
-                .orcid(USER_1_ORCID)
+                .orcid(USER_1_ORCID_URL)
                 .build();
 
         /* test */
@@ -224,7 +224,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
                 .firstname(USER_1_FIRSTNAME)
                 .lastname(USER_1_LASTNAME)
                 .affiliation(USER_1_AFFILIATION)
-                .orcid(USER_1_ORCID)
+                .orcid(USER_1_ORCID_URL)
                 .build();
 
         /* test */
@@ -240,7 +240,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
                 .firstname(USER_1_FIRSTNAME)
                 .lastname(USER_1_LASTNAME)
                 .affiliation(USER_1_AFFILIATION)
-                .orcid(USER_1_ORCID)
+                .orcid(USER_1_ORCID_URL)
                 .build();
 
         /* test */
@@ -512,7 +512,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         doNothing()
                 .when(userService)
                 .updatePassword(USER_1, data);
-        when(databaseService.findAllAccess(USER_1_ID))
+        when(databaseService.findAllAtLestReadAccess(USER_1_ID))
                 .thenReturn(List.of(DATABASE_1));
         doNothing()
                 .when(databaseService)
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java
index ccd8b067de4d002a5f358f47734b85f86ff6cfc8..2815dd6c0557a31490dff959bcff7f60e559b805 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java
@@ -214,9 +214,9 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_3_USERNAME, authorities = {"delete-database-view"})
-    public void delete_publicOwner_succeeds() throws NotAllowedException, DataServiceException,
-            DataServiceConnectionException, DatabaseNotFoundException, AccessNotFoundException,
-            SearchServiceException, SearchServiceConnectionException, ViewNotFoundException {
+    public void delete_publicOwner_succeeds() throws NotAllowedException, DataServiceException, UserNotFoundException,
+            DataServiceConnectionException, DatabaseNotFoundException, AccessNotFoundException, ViewNotFoundException,
+            SearchServiceException, SearchServiceConnectionException {
 
         /* test */
         delete_generic(DATABASE_3_ID, DATABASE_3, VIEW_5_ID, VIEW_5, USER_3_PRINCIPAL, USER_3_ID, USER_3, DATABASE_3_USER_1_WRITE_ALL_ACCESS);
@@ -372,7 +372,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     @WithMockUser(username = USER_1_USERNAME, authorities = {"delete-database-view"})
     public void delete_privateOwner_succeeds() throws NotAllowedException, DataServiceException,
             DataServiceConnectionException, DatabaseNotFoundException, AccessNotFoundException, SearchServiceException,
-            SearchServiceConnectionException, ViewNotFoundException {
+            SearchServiceConnectionException, ViewNotFoundException, UserNotFoundException {
 
         /* test */
         delete_generic(DATABASE_1_ID, DATABASE_1, VIEW_1_ID, VIEW_1, USER_1_PRINCIPAL, USER_1_ID, USER_1, DATABASE_1_USER_1_WRITE_ALL_ACCESS);
@@ -411,7 +411,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-view-visibility"})
     public void update_succeeds() throws NotAllowedException, DataServiceConnectionException, DatabaseNotFoundException,
-            SearchServiceException, SearchServiceConnectionException, ViewNotFoundException {
+            SearchServiceException, SearchServiceConnectionException, ViewNotFoundException, UserNotFoundException {
 
         /* test */
         update_generic(USER_1_PRINCIPAL);
@@ -429,7 +429,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
         when(databaseService.findById(databaseId))
                 .thenReturn(database);
         if (principal != null) {
-            when(userService.findByUsername(user.getUsername()))
+            when(userService.findById(userId))
                     .thenReturn(user);
         }
         if (access != null) {
@@ -471,7 +471,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
         when(databaseService.findById(databaseId))
                 .thenReturn(database);
         if (principal != null) {
-            when(userService.findByUsername(principal.getName()))
+            when(userService.findById(userId))
                     .thenReturn(user);
         }
         if (access != null) {
@@ -511,7 +511,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
                     .thenThrow(AccessNotFoundException.class);
         }
         if (principal != null) {
-            when(userService.findByUsername(principal.getName()))
+            when(userService.findById(userId))
                     .thenReturn(user);
             when(viewService.findById(any(Database.class), anyLong()))
                     .thenReturn(VIEW_1);
@@ -531,7 +531,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     protected void delete_generic(Long databaseId, Database database, Long viewId, View view, Principal principal,
                                   UUID userId, User user, DatabaseAccess access) throws NotAllowedException,
             DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, AccessNotFoundException,
-            SearchServiceException, SearchServiceConnectionException, ViewNotFoundException {
+            SearchServiceException, SearchServiceConnectionException, ViewNotFoundException, UserNotFoundException {
 
         /* mock */
         when(databaseService.findById(databaseId))
@@ -556,7 +556,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
     protected void update_generic(Principal principal) throws SearchServiceException, NotAllowedException,
             DatabaseNotFoundException, SearchServiceConnectionException, DataServiceConnectionException,
-            ViewNotFoundException {
+            ViewNotFoundException, UserNotFoundException {
         final ViewUpdateDto request = ViewUpdateDto.builder()
                 .isPublic(true)
                 .isSchemaPublic(true)
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java
index fc2a9c2c220c4fa601138ee9ee8333d3cd67f493..32a8c8b1907c816d4000e544b2ec0e9d7e8a01fc 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java
@@ -113,7 +113,7 @@ public class BrokerServiceGatewayUnitTest extends AbstractUnitTest {
 
     @Test
     public void grantVirtualHostPermission_invalidResponseCode2_fails() {
-        final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.ACCEPTED)
+        final ResponseEntity<Void> mock = ResponseEntity.accepted()
                 .build();
 
         /* mock */
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java
index 698f7ed524c13bbb3d9fbe5fca37003419f51a4c..daeb1c1a96340323c85ca15080a3bc9d2685823e 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java
@@ -116,7 +116,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
@@ -184,7 +184,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), eq(HttpEntity.EMPTY), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
@@ -322,7 +322,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
@@ -502,7 +502,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
@@ -653,7 +653,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
 
         /* mock */
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), eq(HttpEntity.EMPTY), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java
index aa1c9d4f056f61680aeb341f2cebafdbe262a2b5..b1ce21d4e5e8315b08087dc0d85712509a07973e 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java
@@ -37,7 +37,7 @@ public class SearchServiceGatewayUnitTest extends AbstractUnitTest {
     @Test
     public void update_succeeds() throws DatabaseNotFoundException, SearchServiceException,
             SearchServiceConnectionException {
-        final ResponseEntity<DatabaseDto> mock = ResponseEntity.status(HttpStatus.ACCEPTED)
+        final ResponseEntity<DatabaseDto> mock = ResponseEntity.accepted()
                 .build();
 
         /* mock */
@@ -109,7 +109,7 @@ public class SearchServiceGatewayUnitTest extends AbstractUnitTest {
     @Test
     public void delete_succeeds() throws DatabaseNotFoundException, SearchServiceException,
             SearchServiceConnectionException {
-        final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.ACCEPTED)
+        final ResponseEntity<Void> mock = ResponseEntity.accepted()
                 .build();
 
         /* mock */
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java
index fb574f35cae9dd3f3e10ae53be29d2d2b87aa8ff..9b778e67fb3243e238053972add0f3522c07b9d6 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java
@@ -1,15 +1,15 @@
 package at.tuwien.mapper;
 
 import at.tuwien.api.database.DatabaseDto;
+import at.tuwien.api.database.ViewBriefDto;
 import at.tuwien.api.database.ViewDto;
 import at.tuwien.api.database.table.TableBriefDto;
-import at.tuwien.api.identifier.IdentifierBriefDto;
 import at.tuwien.api.identifier.IdentifierDto;
 import at.tuwien.api.identifier.IdentifierTypeDto;
 import at.tuwien.api.user.UserBriefDto;
 import at.tuwien.api.user.UserDto;
-import at.tuwien.entities.container.Container;
-import at.tuwien.entities.identifier.*;
+import at.tuwien.entities.identifier.Identifier;
+import at.tuwien.entities.identifier.IdentifierType;
 import at.tuwien.test.AbstractUnitTest;
 import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.BeforeEach;
@@ -24,7 +24,6 @@ import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.List;
 import java.util.stream.Stream;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -73,49 +72,6 @@ public class MetadataMapperUnitTest extends AbstractUnitTest {
         assertEquals(IdentifierType.DATABASE, metadataMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.DATABASE));
     }
 
-    @Test
-    public void identifierCreateDtoToIdentifier_succeeds() {
-
-        /* test */
-        final Identifier response = metadataMapper.identifierCreateDtoToIdentifier(IDENTIFIER_1_CREATE_DTO);
-        assertNotNull(response.getTitles());
-        final List<IdentifierTitle> titles = response.getTitles();
-        assertEquals(2, titles.size());
-        final IdentifierTitle title0 = titles.get(0);
-        assertEquals(IDENTIFIER_1_TITLE_1_TITLE, title0.getTitle());
-        assertEquals(IDENTIFIER_1_TITLE_1_LANG, title0.getLanguage());
-        assertEquals(IDENTIFIER_1_TITLE_1_TYPE, title0.getTitleType());
-        final IdentifierTitle title1 = titles.get(1);
-        assertEquals(IDENTIFIER_1_TITLE_2_TITLE, title1.getTitle());
-        assertEquals(IDENTIFIER_1_TITLE_2_LANG, title1.getLanguage());
-        assertEquals(IDENTIFIER_1_TITLE_2_TYPE, title1.getTitleType());
-        assertNotNull(response.getDescriptions());
-        assertEquals(1, response.getDescriptions().size());
-        final List<IdentifierDescription> descriptions = response.getDescriptions();
-        final IdentifierDescription description0 = descriptions.get(0);
-        assertNull(description0.getId());
-        assertEquals(IDENTIFIER_1_DESCRIPTION_1_DESCRIPTION, description0.getDescription());
-        assertEquals(IDENTIFIER_1_DESCRIPTION_1_LANG, description0.getLanguage());
-        assertEquals(IDENTIFIER_1_DESCRIPTION_1_TYPE, description0.getDescriptionType());
-        assertNotNull(response.getCreators());
-        assertEquals(1, response.getCreators().size());
-        final Creator creator0 = response.getCreators().get(0);
-        assertNotNull(creator0);
-        assertNull(creator0.getId());
-        assertEquals(IDENTIFIER_1_CREATOR_1_FIRSTNAME, creator0.getFirstname());
-        assertEquals(IDENTIFIER_1_CREATOR_1_LASTNAME, creator0.getLastname());
-        assertEquals(IDENTIFIER_1_CREATOR_1_NAME, creator0.getCreatorName());
-        assertEquals(IDENTIFIER_1_CREATOR_1_ORCID, creator0.getNameIdentifier());
-        assertEquals(IDENTIFIER_1_CREATOR_1_IDENTIFIER_SCHEME_TYPE, creator0.getNameIdentifierScheme());
-        assertEquals(IDENTIFIER_1_CREATOR_1_AFFILIATION, creator0.getAffiliation());
-        assertEquals(IDENTIFIER_1_CREATOR_1_AFFILIATION_IDENTIFIER, creator0.getAffiliationIdentifier());
-        assertEquals(IDENTIFIER_1_CREATOR_1_AFFILIATION_IDENTIFIER_SCHEME, creator0.getAffiliationIdentifierScheme());
-        assertEquals(IDENTIFIER_1_CREATOR_1_AFFILIATION_IDENTIFIER_SCHEME_URI, creator0.getAffiliationIdentifierSchemeUri());
-        assertNotNull(response.getFunders());
-        assertEquals(1, response.getFunders().size());
-        assertNull(response.getRelatedIdentifiers()); /* mapstruct strategy for empty values is to set null */
-    }
-
     @Test
     public void identifierCreateDtoToIdentifier_withDoi_succeeds() {
 
@@ -142,73 +98,6 @@ public class MetadataMapperUnitTest extends AbstractUnitTest {
         assertEquals(IDENTIFIER_2_TYPE, response.getType());
     }
 
-    @Test
-    public void identifierCreateDtoToIdentifier_view_succeeds() {
-
-        /* test */
-        final Identifier response = metadataMapper.identifierCreateDtoToIdentifier(IDENTIFIER_3_CREATE_DTO);
-        assertNull(response.getDatabase());
-        assertNull(response.getQueryId());
-        assertNull(response.getTableId());
-        assertEquals(IDENTIFIER_3_VIEW_ID, response.getViewId());
-        assertNull(response.getDoi());
-        assertEquals(IDENTIFIER_3_TYPE, response.getType());
-    }
-
-    @Test
-    public void customDatabaseToDatabaseDto_succeeds() {
-
-        /* test */
-        final DatabaseDto response = metadataMapper.customDatabaseToDatabaseDto(DATABASE_1, USER_1);
-        assertEquals(DATABASE_1_ID, response.getId());
-        assertNotNull(response.getContact());
-        assertEquals(USER_1_ID, response.getContact().getId());
-        assertEquals(DATABASE_1_PUBLIC, response.getIsPublic());
-        assertEquals(DATABASE_1_SCHEMA_PUBLIC, response.getIsSchemaPublic());
-        /* identifiers formatted */
-        assertEquals(4, response.getIdentifiers().size());
-        final IdentifierBriefDto identifier1 = response.getIdentifiers().get(0);
-        assertEquals(DATABASE_1_ID, identifier1.getDatabaseId());
-        final IdentifierBriefDto identifier2 = response.getIdentifiers().get(1);
-        assertEquals(DATABASE_1_ID, identifier2.getDatabaseId());
-        final IdentifierBriefDto identifier3 = response.getIdentifiers().get(2);
-        assertEquals(DATABASE_1_ID, identifier3.getDatabaseId());
-        final IdentifierBriefDto identifier4 = response.getIdentifiers().get(3);
-        assertEquals(DATABASE_1_ID, identifier4.getDatabaseId());
-        /* Table 1 formatted */
-        final TableBriefDto table0 = response.getTables().get(0);
-        assertEquals(TABLE_1_ID, table0.getId());
-        assertEquals(TABLE_1_NAME, table0.getName());
-        assertEquals(TABLE_1_INTERNAL_NAME, table0.getInternalName());
-        assertEquals(TABLE_1_DESCRIPTION, table0.getDescription());
-        assertEquals(DATABASE_1_ID, table0.getDatabaseId());
-        assertEquals(TABLE_1_SCHEMA_PUBLIC, table0.getIsSchemaPublic());
-        /* Table 2 formatted */
-        final TableBriefDto table1 = response.getTables().get(1);
-        assertEquals(TABLE_2_ID, table1.getId());
-        assertEquals(TABLE_2_NAME, table1.getName());
-        assertEquals(TABLE_2_INTERNALNAME, table1.getInternalName());
-        assertEquals(TABLE_2_DESCRIPTION, table1.getDescription());
-        assertEquals(DATABASE_1_ID, table1.getDatabaseId());
-        assertEquals(TABLE_2_SCHEMA_PUBLIC, table1.getIsSchemaPublic());
-        /* Table 3 formatted */
-        final TableBriefDto table2 = response.getTables().get(2);
-        assertEquals(TABLE_3_ID, table2.getId());
-        assertEquals(TABLE_3_NAME, table2.getName());
-        assertEquals(TABLE_3_INTERNALNAME, table2.getInternalName());
-        assertEquals(TABLE_3_DESCRIPTION, table2.getDescription());
-        assertEquals(DATABASE_1_ID, table2.getDatabaseId());
-        assertEquals(TABLE_3_SCHEMA_PUBLIC, table2.getIsSchemaPublic());
-        /* Table 4 formatted */
-        final TableBriefDto table3 = response.getTables().get(3);
-        assertEquals(TABLE_4_ID, table3.getId());
-        assertEquals(TABLE_4_NAME, table3.getName());
-        assertEquals(TABLE_4_INTERNALNAME, table3.getInternalName());
-        assertEquals(TABLE_4_DESCRIPTION, table3.getDescription());
-        assertEquals(DATABASE_1_ID, table3.getDatabaseId());
-        assertEquals(TABLE_4_SCHEMA_PUBLIC, table3.getIsSchemaPublic());
-    }
-
     public static Stream<Arguments> nameToInternalName_parameters() {
         return Stream.of(
                 Arguments.arguments("dash_minus", "OE/NO-027", "oe_no_027"),
@@ -235,61 +124,104 @@ public class MetadataMapperUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    public void userEquals_identity_succeeds() {
+    public void userToUserBriefDto_succeeds() {
 
         /* test */
-        assertEquals(USER_1_DTO, USER_1_DTO);
+        final UserBriefDto response = metadataMapper.userToUserBriefDto(USER_1);
+        assertEquals(USER_1_NAME, response.getName());
+        assertEquals(USER_1_NAME + " — @" + USER_1_USERNAME, response.getQualifiedName());
     }
 
     @Test
-    public void userEquals_similar_succeeds() {
-        final UserDto tmp = UserDto.builder()
-                .id(USER_1_ID)
-                .build();
+    public void userToUserDto_succeeds() {
 
         /* test */
-        assertEquals(USER_1_DTO, tmp);
+        assertEquals(USER_1_DTO, metadataMapper.userToUserDto(USER_1));
+        assertEquals(USER_2_DTO, metadataMapper.userToUserDto(USER_2));
+        assertEquals(USER_3_DTO, metadataMapper.userToUserDto(USER_3));
+        assertEquals(USER_4_DTO, metadataMapper.userToUserDto(USER_4));
+        assertEquals(USER_5_DTO, metadataMapper.userToUserDto(USER_5));
     }
 
     @Test
-    public void userToUserBriefDto_succeeds() {
+    public void identifierToIdentifierDto_succeeds() {
 
         /* test */
-        final UserBriefDto response = metadataMapper.userToUserBriefDto(USER_1);
-        assertEquals(USER_1_NAME, response.getName());
-        assertEquals(USER_1_NAME + " — @" + USER_1_USERNAME, response.getQualifiedName());
+        assertEquals(IDENTIFIER_1_DTO, metadataMapper.identifierToIdentifierDto(IDENTIFIER_1));
+        assertEquals(IDENTIFIER_2_DTO, metadataMapper.identifierToIdentifierDto(IDENTIFIER_2));
+        assertEquals(IDENTIFIER_3_DTO, metadataMapper.identifierToIdentifierDto(IDENTIFIER_3));
+        assertEquals(IDENTIFIER_4_DTO, metadataMapper.identifierToIdentifierDto(IDENTIFIER_4));
+        assertEquals(IDENTIFIER_5_DTO, metadataMapper.identifierToIdentifierDto(IDENTIFIER_5));
+        assertEquals(IDENTIFIER_6_DTO, metadataMapper.identifierToIdentifierDto(IDENTIFIER_6));
+        assertEquals(IDENTIFIER_7_DTO, metadataMapper.identifierToIdentifierDto(IDENTIFIER_7));
     }
 
     @Test
-    public void userToUserDto_succeeds() {
+    public void viewToViewDto_succeeds() {
 
         /* test */
-        final UserDto response = metadataMapper.userToUserDto(USER_1);
-        assertEquals(USER_1_NAME, response.getName());
-        assertEquals(USER_1_NAME + " — @" + USER_1_USERNAME, response.getQualifiedName());
+        assertEquals(VIEW_1_DTO, metadataMapper.viewToViewDto(VIEW_1));
+        assertEquals(VIEW_2_DTO, metadataMapper.viewToViewDto(VIEW_2));
+        assertEquals(VIEW_3_DTO, metadataMapper.viewToViewDto(VIEW_3));
+        assertEquals(VIEW_4_DTO, metadataMapper.viewToViewDto(VIEW_4));
+        assertEquals(VIEW_5_DTO, metadataMapper.viewToViewDto(VIEW_5));
     }
 
     @Test
-    public void viewToViewDto_succeeds() {
+    public void tableToTableBriefDto_succeeds() {
+
+        /* test */
+        assertEquals(TABLE_1_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_1));
+        assertEquals(TABLE_2_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_2));
+        assertEquals(TABLE_3_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_3));
+        assertEquals(TABLE_4_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_4));
+        assertEquals(TABLE_5_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_5));
+        assertEquals(TABLE_6_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_6));
+        assertEquals(TABLE_7_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_7));
+        assertEquals(TABLE_8_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_8));
+        assertEquals(TABLE_9_BRIEF_DTO, metadataMapper.tableToTableBriefDto(TABLE_9));
+    }
+
+    @Test
+    public void containerToContainerBriefDto_succeeds() {
+
+        /* test */
+        assertEquals(CONTAINER_1_BRIEF_DTO, metadataMapper.containerToContainerBriefDto(CONTAINER_1));
+    }
+
+    @Test
+    public void bannerMessageToBannerMessageDto_succeeds() {
+
+        /* test */
+        assertEquals(BANNER_MESSAGE_1_DTO, metadataMapper.bannerMessageToBannerMessageDto(BANNER_MESSAGE_1));
+    }
+
+    @Test
+    public void containerImageToImageBriefDto_succeeds() {
+
+        /* test */
+        assertEquals(IMAGE_1_BRIEF_DTO, metadataMapper.containerImageToImageBriefDto(IMAGE_1));
+    }
+
+    @Test
+    public void containerImageToImageDto_succeeds() {
+
+        /* test */
+        assertEquals(IMAGE_1_DTO, metadataMapper.containerImageToImageDto(IMAGE_1));
+    }
+
+    @Test
+    public void ontologyToOntologyBriefDto_succeeds() {
+
+        /* test */
+        assertEquals(ONTOLOGY_1_BRIEF_DTO, metadataMapper.ontologyToOntologyBriefDto(ONTOLOGY_1));
+    }
+
+    @Test
+    public void ontologyToOntologyDto_succeeds() {
 
         /* test */
-        final ViewDto response = metadataMapper.viewToViewDto(VIEW_1);
-        assertEquals(VIEW_1_ID, response.getId());
-        assertEquals(VIEW_1_DATABASE_ID, response.getVdbid());
-        assertEquals(VIEW_1_NAME, response.getName());
-        assertEquals(VIEW_1_INTERNAL_NAME, response.getInternalName());
-        assertNotNull(response.getDatabase());
-        assertEquals(VIEW_1_DATABASE_ID, response.getDatabase().getId());
-        assertEquals(VIEW_1_QUERY, response.getQuery());
-        assertEquals(VIEW_1_QUERY_HASH, response.getQueryHash());
-        assertNotNull(response.getIdentifiers());
-        assertEquals(1, response.getIdentifiers().size());
-        final IdentifierDto identifier0 = response.getIdentifiers().get(0);
-        assertEquals(IDENTIFIER_3_ID, identifier0.getId());
-        assertEquals(VIEW_1_DATABASE_ID, identifier0.getDatabaseId());
-        assertEquals(VIEW_1_ID, identifier0.getViewId());
-        assertEquals(VIEW_1_QUERY, identifier0.getQuery());
-        assertEquals(VIEW_1_QUERY_HASH, identifier0.getQueryHash());
+        assertEquals(ONTOLOGY_1_DTO, metadataMapper.ontologyToOntologyDto(ONTOLOGY_1));
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java
index 3f69453649dac695a7939ffe2881eb02d855c79d..eec5aebf4bea668164c51bae655c074cb306e4c2 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java
@@ -1,5 +1,6 @@
 package at.tuwien.mvc;
 
+import at.tuwien.api.keycloak.TokenDto;
 import at.tuwien.exception.AuthServiceConnectionException;
 import at.tuwien.exception.AuthServiceException;
 import at.tuwien.exception.CredentialsInvalidException;
@@ -8,6 +9,8 @@ import at.tuwien.repository.ContainerRepository;
 import at.tuwien.repository.DatabaseRepository;
 import at.tuwien.repository.LicenseRepository;
 import at.tuwien.repository.UserRepository;
+import at.tuwien.service.AuthenticationService;
+import at.tuwien.service.UserService;
 import at.tuwien.test.AbstractUnitTest;
 import at.tuwien.utils.KeycloakUtils;
 import dasniko.testcontainers.keycloak.KeycloakContainer;
@@ -18,7 +21,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.DynamicPropertyRegistry;
 import org.springframework.test.context.DynamicPropertySource;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -28,10 +30,8 @@ import org.testcontainers.junit.jupiter.Container;
 import org.testcontainers.junit.jupiter.Testcontainers;
 
 import java.util.List;
-import java.util.Optional;
 
-import static org.mockito.Mockito.when;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
@@ -65,8 +65,11 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
     @Autowired
     private DatabaseRepository databaseRepository;
 
+    @Autowired
+    private AuthenticationService authenticationService;
+
     @Container
-    private static KeycloakContainer keycloakContainer = new KeycloakContainer("quay.io/keycloak/keycloak:24.0")
+    private static KeycloakContainer keycloakContainer = new KeycloakContainer(KEYCLOAK_IMAGE)
             .withImagePullPolicy(PullPolicy.alwaysPull())
             .withAdminUsername("admin")
             .withAdminPassword("admin")
@@ -88,10 +91,11 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
         databaseRepository.save(DATABASE_1);
         /* keycloak */
         keycloakUtils.deleteUser(USER_1_USERNAME);
+        keycloakUtils.deleteUser(USER_LOCAL_ADMIN_USERNAME);
     }
 
     @Test
-    public void findById_database_basicUser_fails() throws Exception {
+    public void findById_database_basicUser_succeeds() throws Exception {
 
         /* mock */
         keycloakGateway.createUser(USER_1_KEYCLOAK_SIGNUP_REQUEST);
@@ -108,11 +112,11 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
     @Test
     public void findById_database_basicAdmin_succeeds() throws Exception {
 
-        /* mock */
-        keycloakGateway.createUser(USER_1_KEYCLOAK_SYSTEM_SIGNUP_REQUEST);
+        /* pre condition */
+        keycloakGateway.createUser(USER_LOCAL_KEYCLOAK_SIGNUP_REQUEST);
 
         /* test */
-        this.mockMvc.perform(get("/api/database/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
+        this.mockMvc.perform(get("/api/database/1").with(httpBasic(USER_LOCAL_ADMIN_USERNAME, USER_LOCAL_ADMIN_PASSWORD)))
                 .andDo(print())
                 .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
                 .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
@@ -123,11 +127,14 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
     }
 
     @Test
-    @WithMockUser(username = "admin", authorities = {"system"})
     public void findById_database_bearerAdmin_succeeds() throws Exception {
 
+        /* pre condition */
+        keycloakGateway.createUser(USER_LOCAL_KEYCLOAK_SIGNUP_REQUEST);
+        final TokenDto jwt = authenticationService.obtainToken(USER_LOCAL_ADMIN_LOGIN_REQUEST_DTO);
+
         /* test */
-        this.mockMvc.perform(get("/api/database/1"))
+        this.mockMvc.perform(get("/api/database/1").header("Authorization", "Bearer " + jwt.getAccessToken()))
                 .andDo(print())
                 .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
                 .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
@@ -138,51 +145,15 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
     }
 
     @Test
-    public void findById_table_basicUser_fails() throws Exception {
-
-        /* mock */
-        keycloakGateway.createUser(USER_1_KEYCLOAK_SIGNUP_REQUEST);
-
-        /* test */
-        this.mockMvc.perform(get("/api/database/1/table/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
-                .andDo(print())
-                .andExpect(header().doesNotExist("X-Username"))
-                .andExpect(header().doesNotExist("X-Password"))
-                .andExpect(header().doesNotExist("X-Host"))
-                .andExpect(header().doesNotExist("X-Port"))
-                .andExpect(header().doesNotExist("X-Type"))
-                .andExpect(header().doesNotExist("X-Database"))
-                .andExpect(header().doesNotExist("X-Table"))
-                .andExpect(header().doesNotExist("Access-Control-Expose-Headers"))
-                .andExpect(status().isOk());
-    }
-
-    @Test
-    public void findById_table_basicAdmin_succeeds() throws Exception {
+    public void findById_table_bearerAdmin_succeeds() throws Exception {
 
-        /* mock */
-        keycloakGateway.createUser(USER_1_KEYCLOAK_SYSTEM_SIGNUP_REQUEST);
+        /* pre condition */
+        keycloakGateway.createUser(USER_LOCAL_KEYCLOAK_SIGNUP_REQUEST);
+        final TokenDto jwt = authenticationService.obtainToken(USER_LOCAL_ADMIN_LOGIN_REQUEST_DTO);
 
-        /* test */
-        this.mockMvc.perform(get("/api/database/1/table/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
-                .andDo(print())
-                .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
-                .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
-                .andExpect(header().string("X-Host", CONTAINER_1_HOST))
-                .andExpect(header().string("X-Port", "" + CONTAINER_1_PORT))
-                .andExpect(header().string("X-Type", IMAGE_1_JDBC))
-                .andExpect(header().string("X-Database", DATABASE_1_INTERNALNAME))
-                .andExpect(header().string("X-Table", TABLE_1_INTERNAL_NAME))
-                .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-Table"))
-                .andExpect(status().isOk());
-    }
-
-    @Test
-    @WithMockUser(username = "admin", authorities = {"system"})
-    public void findById_table_bearerAdmin_succeeds() throws Exception {
 
         /* test */
-        this.mockMvc.perform(get("/api/database/1/table/1"))
+        this.mockMvc.perform(get("/api/database/1/table/1").header("Authorization", "Bearer " + jwt.getAccessToken()))
                 .andDo(print())
                 .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
                 .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
@@ -196,13 +167,13 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
     }
 
     @Test
-    public void findById_view_basicUser_fails() throws Exception {
+    public void findById_table_basicUser_succeeds() throws Exception {
 
         /* mock */
         keycloakGateway.createUser(USER_1_KEYCLOAK_SIGNUP_REQUEST);
 
         /* test */
-        this.mockMvc.perform(get("/api/database/1/view/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
+        this.mockMvc.perform(get("/api/database/1/table/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
                 .andDo(print())
                 .andExpect(header().doesNotExist("X-Username"))
                 .andExpect(header().doesNotExist("X-Password"))
@@ -210,37 +181,19 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
                 .andExpect(header().doesNotExist("X-Port"))
                 .andExpect(header().doesNotExist("X-Type"))
                 .andExpect(header().doesNotExist("X-Database"))
-                .andExpect(header().doesNotExist("X-View"))
+                .andExpect(header().doesNotExist("X-Table"))
                 .andExpect(header().doesNotExist("Access-Control-Expose-Headers"))
                 .andExpect(status().isOk());
     }
 
     @Test
-    public void findById_view_basicAdmin_succeeds() throws Exception {
+    public void findById_table_basicAdmin_succeeds() throws Exception {
 
         /* mock */
-        keycloakGateway.createUser(USER_1_KEYCLOAK_SYSTEM_SIGNUP_REQUEST);
-
-        /* test */
-        this.mockMvc.perform(get("/api/database/1/view/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
-                .andDo(print())
-                .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
-                .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
-                .andExpect(header().string("X-Host", CONTAINER_1_HOST))
-                .andExpect(header().string("X-Port", "" + CONTAINER_1_PORT))
-                .andExpect(header().string("X-Type", IMAGE_1_JDBC))
-                .andExpect(header().string("X-Database", DATABASE_1_INTERNALNAME))
-                .andExpect(header().string("X-View", VIEW_1_INTERNAL_NAME))
-                .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-View"))
-                .andExpect(status().isOk());
-    }
-
-    @Test
-    @WithMockUser(username = "admin", authorities = {"system"})
-    public void findById_view_bearerAdmin_succeeds() throws Exception {
+        keycloakGateway.createUser(USER_LOCAL_KEYCLOAK_SIGNUP_REQUEST);
 
         /* test */
-        this.mockMvc.perform(get("/api/database/1/view/1"))
+        this.mockMvc.perform(get("/api/database/1/table/1").with(httpBasic(USER_LOCAL_ADMIN_USERNAME, USER_LOCAL_ADMIN_PASSWORD)))
                 .andDo(print())
                 .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
                 .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
@@ -248,51 +201,43 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest
                 .andExpect(header().string("X-Port", "" + CONTAINER_1_PORT))
                 .andExpect(header().string("X-Type", IMAGE_1_JDBC))
                 .andExpect(header().string("X-Database", DATABASE_1_INTERNALNAME))
-                .andExpect(header().string("X-View", VIEW_1_INTERNAL_NAME))
-                .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-View"))
+                .andExpect(header().string("X-Table", TABLE_1_INTERNAL_NAME))
+                .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-Table"))
                 .andExpect(status().isOk());
     }
 
     @Test
-    public void findById_container_basicUser_fails() throws Exception {
+    public void findById_view_basicUser_succeeds() throws Exception {
 
         /* mock */
         keycloakGateway.createUser(USER_1_KEYCLOAK_SIGNUP_REQUEST);
 
         /* test */
-        this.mockMvc.perform(get("/api/container/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
+        this.mockMvc.perform(get("/api/database/1/view/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
                 .andDo(print())
                 .andExpect(header().doesNotExist("X-Username"))
                 .andExpect(header().doesNotExist("X-Password"))
+                .andExpect(header().doesNotExist("X-Host"))
+                .andExpect(header().doesNotExist("X-Port"))
+                .andExpect(header().doesNotExist("X-Type"))
+                .andExpect(header().doesNotExist("X-Database"))
+                .andExpect(header().doesNotExist("X-View"))
                 .andExpect(header().doesNotExist("Access-Control-Expose-Headers"))
                 .andExpect(status().isOk());
     }
 
     @Test
-    public void findById_container_basicAdmin_succeeds() throws Exception {
+    public void findById_container_basicUser_succeeds() throws Exception {
 
         /* mock */
-        keycloakGateway.createUser(USER_1_KEYCLOAK_SYSTEM_SIGNUP_REQUEST);
+        keycloakGateway.createUser(USER_1_KEYCLOAK_SIGNUP_REQUEST);
 
         /* test */
         this.mockMvc.perform(get("/api/container/1").with(httpBasic(USER_1_USERNAME, USER_1_PASSWORD)))
                 .andDo(print())
-                .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
-                .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
-                .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password"))
-                .andExpect(status().isOk());
-    }
-
-    @Test
-    @WithMockUser(username = "admin", authorities = {"system"})
-    public void findById_container_bearerAdmin_succeeds() throws Exception {
-
-        /* test */
-        this.mockMvc.perform(get("/api/container/1"))
-                .andDo(print())
-                .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME))
-                .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD))
-                .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password"))
+                .andExpect(header().doesNotExist("X-Username"))
+                .andExpect(header().doesNotExist("X-Password"))
+                .andExpect(header().doesNotExist("Access-Control-Expose-Headers"))
                 .andExpect(status().isOk());
     }
 
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/IdentifierEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/IdentifierEndpointMvcTest.java
index e4cdcdbdd884a3a1f28f0a20a244824f6ac3702c..2594eb70b1240d7ba39c866e2ee8c900cf191ea7 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/IdentifierEndpointMvcTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/IdentifierEndpointMvcTest.java
@@ -42,7 +42,7 @@ public class IdentifierEndpointMvcTest extends AbstractUnitTest {
                 .thenReturn(ORCID_1_DTO);
 
         /* test */
-        this.mockMvc.perform(get("/api/identifier/retrieve?url=http://orcid.org/" + USER_1_ORCID_UNCOMPRESSED)
+        this.mockMvc.perform(get("/api/identifier/retrieve?url=" + USER_1_ORCID_URL)
                         .contentType(MediaType.APPLICATION_JSON)
                         .accept(MediaType.APPLICATION_JSON))
                 .andExpect(content().string(FileUtils.getContentsAsString(new File("src/test/resources/json/ext_orcid_jdoe.json"))))
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
index 578f6276c791ab8465b46f115acc05ace1b7e948..5ae4aad0187cc52749416bdb3208d3ff678d5285 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
@@ -202,7 +202,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
 
         /* mock */
         try {
-            databaseEndpoint.list(null);
+            databaseEndpoint.list(null, null);
         } catch (Exception e) {
             /* ignore */
         }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java
index 54015177aaff7d3e52a98b5c2db7103a8c272fc1..c647cdbd7403b5f5c742953fd9978e0c8d98ab06 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java
@@ -81,7 +81,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
                 .thenReturn(ResponseEntity.status(HttpStatus.CREATED)
                         .build());
         when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
@@ -228,10 +228,10 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
@@ -301,7 +301,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpClientErrorException.BadRequest.class)
                 .when(searchServiceRestTemplate)
@@ -320,7 +320,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpClientErrorException.Unauthorized.class)
                 .when(searchServiceRestTemplate)
@@ -339,7 +339,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpClientErrorException.NotFound.class)
                 .when(searchServiceRestTemplate)
@@ -358,7 +358,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpServerErrorException.InternalServerError.class)
                 .when(searchServiceRestTemplate)
@@ -380,10 +380,10 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
 
         /* test */
@@ -441,7 +441,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpClientErrorException.BadRequest.class)
                 .when(searchServiceRestTemplate)
@@ -462,7 +462,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpClientErrorException.Unauthorized.class)
                 .when(searchServiceRestTemplate)
@@ -483,7 +483,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpClientErrorException.NotFound.class)
                 .when(searchServiceRestTemplate)
@@ -504,7 +504,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest {
         when(databaseRepository.save(any(Database.class)))
                 .thenReturn(DATABASE_1);
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class)))
-                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                .thenReturn(ResponseEntity.accepted()
                         .build());
         doThrow(HttpServerErrorException.InternalServerError.class)
                 .when(searchServiceRestTemplate)
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java
index bda2fc0ee5b103393a14b32b4244018c242d3f5a..9c91084f77cdac6c554cb11ea847bbb447185283 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java
@@ -1,5 +1,6 @@
 package at.tuwien.service;
 
+import at.tuwien.entities.container.Container;
 import at.tuwien.entities.database.Database;
 import at.tuwien.exception.DatabaseNotFoundException;
 import at.tuwien.repository.ContainerRepository;
@@ -64,11 +65,42 @@ public class DatabaseServicePersistenceTest extends AbstractUnitTest {
 
     @Test
     @Transactional(readOnly = true)
-    public void findByInternalName_succeeds() throws DatabaseNotFoundException {
+    public void findAllPublicOrSchemaPublicByInternalName_succeeds() {
 
         /* test */
-        final Database response = databaseService.findByInternalName(DATABASE_1_INTERNALNAME);
-        assertEquals(DATABASE_1, response);
+        final List<Database> response = databaseService.findAllPublicOrSchemaPublicByInternalName(DATABASE_3_INTERNALNAME);
+        assertEquals(1, response.size());
+        assertEquals(DATABASE_3, response.get(0));
+    }
+
+    @Test
+    @Transactional(readOnly = true)
+    public void findAllPublicOrSchemaPublicByInternalName_privateEmpty_succeeds() {
+
+        /* test */
+        final List<Database> response = databaseService.findAllPublicOrSchemaPublicByInternalName(DATABASE_1_INTERNALNAME);
+        assertEquals(0, response.size());
+    }
+
+    @Test
+    @Transactional(readOnly = true)
+    public void findAllAtLestReadAccess_privateNoAccessEmpty_succeeds() {
+
+        /* test */
+        final List<Database> response = databaseService.findAllAtLestReadAccess(USER_4_ID);
+        assertEquals(0, response.size());
+    }
+
+    @Test
+    @Transactional(readOnly = true)
+    public void findAllAtLestReadAccess_privateAccess_succeeds() {
+
+        /* test */
+        final List<Database> response = databaseService.findAllAtLestReadAccess(USER_2_ID);
+        assertEquals(3, response.size());
+        assertEquals(DATABASE_4, response.get(0));
+        assertEquals(DATABASE_2, response.get(1));
+        assertEquals(DATABASE_1, response.get(2));
     }
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java
index 7815c4ae69a98c5dfd5171e1c83cd9fdb6de0a72..1b6570abd821337fe032bead021cf9d5a4b9fc8c 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java
@@ -87,25 +87,12 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    public void findByInternalName_notFound_fails() {
-
-        /* mock */
-        when(databaseRepository.findByInternalName(DATABASE_1_INTERNALNAME))
-                .thenReturn(Optional.empty());
-
-        /* test */
-        assertThrows(DatabaseNotFoundException.class, () -> {
-            databaseService.findByInternalName(DATABASE_1_INTERNALNAME);
-        });
-    }
-
     @Test
     public void updatePassword_succeeds() throws DataServiceException, DatabaseNotFoundException,
             DataServiceConnectionException {
 
         /* mock */
-        when(databaseRepository.findReadAccess(USER_1_ID))
+        when(databaseRepository.findAllAtLestReadAccessDesc(USER_1_ID))
                 .thenReturn(List.of(DATABASE_1));
         doNothing()
                 .when(dataServiceGateway)
@@ -535,7 +522,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1);
 
         /* test */
-        final Database response = databaseService.create(CONTAINER_1, DATABASE_1_CREATE, USER_1);
+        final Database response = databaseService.create(CONTAINER_1, DATABASE_1_CREATE, USER_1, List.of(USER_LOCAL));
         assertTrue(response.getInternalName().startsWith(DATABASE_1_INTERNALNAME));
         assertNotNull(response.getContainer());
         assertNotNull(response.getTables());
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/utils/KeycloakUtils.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/utils/KeycloakUtils.java
index 49694e6fd1dc49eb0f4bc8df35f1b6cd4965bf5e..f5ad18b694081ed50b879d6690e2a85748b3bece 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/utils/KeycloakUtils.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/utils/KeycloakUtils.java
@@ -1,18 +1,13 @@
 package at.tuwien.utils;
 
-import at.tuwien.api.keycloak.RoleRepresentationDto;
-import at.tuwien.exception.*;
+import at.tuwien.exception.AuthServiceConnectionException;
+import at.tuwien.exception.AuthServiceException;
+import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.gateway.KeycloakGateway;
-import at.tuwien.gateway.impl.KeycloakGatewayImpl;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.*;
 import org.springframework.stereotype.Component;
-import org.springframework.web.client.HttpClientErrorException;
-import org.springframework.web.client.HttpServerErrorException;
-import org.springframework.web.client.RestTemplate;
 
-import java.util.List;
 import java.util.UUID;
 
 @Log4j2
@@ -28,8 +23,7 @@ public class KeycloakUtils {
         this.keycloakGateway = keycloakGateway;
     }
 
-    public void deleteUser(String username) throws AuthServiceException, AuthServiceConnectionException,
-            CredentialsInvalidException {
+    public void deleteUser(String username) throws AuthServiceException, AuthServiceConnectionException {
         try {
             final UUID userId = keycloakGateway.findByUsername(username).getId();
             keycloakGateway.deleteUser(userId);
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java
index 29718b0962c76b8b1ad01ef003de092459d92714..342a9e328ecc8e69abe7bfedf1827bdc7777fff3 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java
@@ -324,20 +324,38 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    public void validateOnlyPrivateHasRole_privatePrincipalMissing_fails() {
+    public void validateOnlyPrivateDataHasRole_privatePrincipalMissing_fails() {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            endpointValidator.validateOnlyPrivateHasRole(DATABASE_1, null, "list-tables");
+            endpointValidator.validateOnlyPrivateDataHasRole(DATABASE_1, null, "list-tables");
         });
     }
 
     @Test
-    public void validateOnlyPrivateHasRole_privateRoleMissing_fails() {
+    public void validateOnlyPrivateDataHasRole_privateRoleMissing_fails() {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            endpointValidator.validateOnlyPrivateHasRole(DATABASE_1, USER_4_PRINCIPAL, "list-tables");
+            endpointValidator.validateOnlyPrivateDataHasRole(DATABASE_1, USER_4_PRINCIPAL, "list-tables");
+        });
+    }
+
+    @Test
+    public void validateOnlyPrivateSchemaHasRole_privatePrincipalMissing_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            endpointValidator.validateOnlyPrivateSchemaHasRole(DATABASE_1, null, "list-tables");
+        });
+    }
+
+    @Test
+    public void validateOnlyPrivateSchemaHasRole_privateRoleMissing_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            endpointValidator.validateOnlyPrivateSchemaHasRole(DATABASE_1, USER_4_PRINCIPAL, "list-tables");
         });
     }
 
diff --git a/dbrepo-metadata-service/rest-service/src/test/resources/init/dbrepo-realm.json b/dbrepo-metadata-service/rest-service/src/test/resources/init/dbrepo-realm.json
index 588053e15fa6d5872c4cb83da42d71859b5f7a64..7ee28b34a4f2d662bb43979930732dec74da8a63 100644
--- a/dbrepo-metadata-service/rest-service/src/test/resources/init/dbrepo-realm.json
+++ b/dbrepo-metadata-service/rest-service/src/test/resources/init/dbrepo-realm.json
@@ -37,6 +37,7 @@
   "editUsernameAllowed" : false,
   "bruteForceProtected" : false,
   "permanentLockout" : false,
+  "maxTemporaryLockouts" : 0,
   "maxFailureWaitSeconds" : 900,
   "minimumQuickLoginWaitSeconds" : 60,
   "waitIncrementSeconds" : 60,
@@ -1096,34 +1097,34 @@
     "id" : "f2ce17fe-7b15-47a4-bbf8-86f415298fa9",
     "name" : "data-stewards",
     "path" : "/data-stewards",
+    "subGroups" : [ ],
     "attributes" : { },
     "realmRoles" : [ "default-data-steward-roles" ],
-    "clientRoles" : { },
-    "subGroups" : [ ]
+    "clientRoles" : { }
   }, {
     "id" : "124d9888-0b6e-46aa-8225-077dcedaf16e",
     "name" : "developers",
     "path" : "/developers",
+    "subGroups" : [ ],
     "attributes" : { },
     "realmRoles" : [ "default-developer-roles" ],
-    "clientRoles" : { },
-    "subGroups" : [ ]
+    "clientRoles" : { }
   }, {
     "id" : "f467c38e-9041-4faa-ae0b-39cec65ff4db",
     "name" : "researchers",
     "path" : "/researchers",
+    "subGroups" : [ ],
     "attributes" : { },
     "realmRoles" : [ "default-researcher-roles" ],
-    "clientRoles" : { },
-    "subGroups" : [ ]
+    "clientRoles" : { }
   }, {
     "id" : "2b9f94b4-d434-4a98-8eab-25678cfee983",
     "name" : "system",
     "path" : "/system",
+    "subGroups" : [ ],
     "attributes" : { },
     "realmRoles" : [ "default-system-roles" ],
-    "clientRoles" : { },
-    "subGroups" : [ ]
+    "clientRoles" : { }
   } ],
   "defaultRole" : {
     "id" : "abd2d9ee-ebc4-4d0a-839e-6b588a6d442a",
@@ -1142,7 +1143,8 @@
   "otpPolicyLookAheadWindow" : 1,
   "otpPolicyPeriod" : 30,
   "otpPolicyCodeReusable" : false,
-  "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName" ],
+  "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ],
+  "localizationTexts" : { },
   "webAuthnPolicyRpEntityName" : "keycloak",
   "webAuthnPolicySignatureAlgorithms" : [ "ES256" ],
   "webAuthnPolicyRpId" : "",
@@ -1153,6 +1155,7 @@
   "webAuthnPolicyCreateTimeout" : 0,
   "webAuthnPolicyAvoidSameAuthenticatorRegister" : false,
   "webAuthnPolicyAcceptableAaguids" : [ ],
+  "webAuthnPolicyExtraOrigins" : [ ],
   "webAuthnPolicyPasswordlessRpEntityName" : "keycloak",
   "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ],
   "webAuthnPolicyPasswordlessRpId" : "",
@@ -1163,6 +1166,7 @@
   "webAuthnPolicyPasswordlessCreateTimeout" : 0,
   "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false,
   "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ],
+  "webAuthnPolicyPasswordlessExtraOrigins" : [ ],
   "scopeMappings" : [ {
     "clientScope" : "rabbitmq.tag:administrator",
     "roles" : [ "escalated-broker-handling" ]
@@ -1365,19 +1369,20 @@
         "access.tokenResponse.claim" : "false"
       }
     }, {
-      "id" : "030a1cd9-53d1-4a62-a375-94d50a2dc6fc",
+      "id" : "0b4c644f-0cf0-4794-8395-d5d83009dabe",
       "name" : "uid",
       "protocol" : "openid-connect",
       "protocolMapper" : "oidc-usermodel-attribute-mapper",
       "consentRequired" : false,
       "config" : {
-        "aggregate.attrs" : "false",
-        "multivalued" : "false",
+        "introspection.token.claim" : "true",
         "userinfo.token.claim" : "true",
-        "user.attribute" : "LDAP_ID",
+        "user.attribute" : "CUSTOM_ID",
         "id.token.claim" : "true",
+        "lightweight.claim" : "false",
         "access.token.claim" : "true",
-        "claim.name" : "uid"
+        "claim.name" : "uid",
+        "jsonType.label" : "String"
       }
     } ],
     "defaultClientScopes" : [ "roles", "attributes" ],
@@ -2074,6 +2079,7 @@
   "browserSecurityHeaders" : {
     "contentSecurityPolicyReportOnly" : "",
     "xContentTypeOptions" : "nosniff",
+    "referrerPolicy" : "no-referrer",
     "xRobotsTag" : "none",
     "xFrameOptions" : "SAMEORIGIN",
     "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
@@ -2107,23 +2113,6 @@
       "config" : {
         "allow-default-scopes" : [ "true" ]
       }
-    }, {
-      "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1",
-      "name" : "Trusted Hosts",
-      "providerId" : "trusted-hosts",
-      "subType" : "anonymous",
-      "subComponents" : { },
-      "config" : {
-        "host-sending-registration-request-must-match" : [ "true" ],
-        "client-uris-must-match" : [ "true" ]
-      }
-    }, {
-      "id" : "f565cb47-3bcf-4078-8f94-eb4179c375b8",
-      "name" : "Full Scope Disabled",
-      "providerId" : "scope",
-      "subType" : "anonymous",
-      "subComponents" : { },
-      "config" : { }
     }, {
       "id" : "0efa669d-1017-4b4a-82e1-c2eaf72de2c9",
       "name" : "Allowed Client Scopes",
@@ -2141,25 +2130,73 @@
       "subComponents" : { },
       "config" : { }
     }, {
-      "id" : "104ec5a9-025b-4c44-8ac0-82d22887ca3e",
+      "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979",
       "name" : "Allowed Protocol Mapper Types",
       "providerId" : "allowed-protocol-mappers",
-      "subType" : "authenticated",
+      "subType" : "anonymous",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper" ]
+        "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "saml-role-list-mapper" ]
       }
     }, {
-      "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979",
+      "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1",
+      "name" : "Trusted Hosts",
+      "providerId" : "trusted-hosts",
+      "subType" : "anonymous",
+      "subComponents" : { },
+      "config" : {
+        "host-sending-registration-request-must-match" : [ "true" ],
+        "client-uris-must-match" : [ "true" ]
+      }
+    }, {
+      "id" : "f565cb47-3bcf-4078-8f94-eb4179c375b8",
+      "name" : "Full Scope Disabled",
+      "providerId" : "scope",
+      "subType" : "anonymous",
+      "subComponents" : { },
+      "config" : { }
+    }, {
+      "id" : "104ec5a9-025b-4c44-8ac0-82d22887ca3e",
       "name" : "Allowed Protocol Mapper Types",
       "providerId" : "allowed-protocol-mappers",
-      "subType" : "anonymous",
+      "subType" : "authenticated",
+      "subComponents" : { },
+      "config" : {
+        "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper" ]
+      }
+    } ],
+    "org.keycloak.userprofile.UserProfileProvider" : [ {
+      "id" : "fb763636-e1ea-49c7-adca-ea105cdec4ad",
+      "providerId" : "declarative-user-profile",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper" ]
+        "kc.user.profile.config" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}" ]
       }
     } ],
     "org.keycloak.keys.KeyProvider" : [ {
+      "id" : "2f53ccf3-37b0-4d34-83e7-ed497499ee51",
+      "name" : "rsa-enc-generated",
+      "providerId" : "rsa-enc-generated",
+      "subComponents" : { },
+      "config" : {
+        "privateKey" : [ "MIIEowIBAAKCAQEA3b1tNLfcjFLUw9UShVDNf+ZD8sQqb4YBaIXcSJTX/zDQUPiCp176BBGI3s4VplDArnOW+LumozmKogeoHEnGEIDW8ovgK5uMU9tSA2p0qqGBUMOdR8YATTIfCJe7qGiiuGa3WZy3sQLM70SuRzx02YU8gvUcvl2Js4KyqAziOUX/w3Wa59H9jjGNUXYyqaPWJp73eHzbVYWySzyLG22mVlcUtBx5siL5T2/Xu0p9z4l7/bapwwmOVi1ZrcHjbEAwdGEiSMGI/uWqAF+r1BRpmJLR7HNXcL3eK4/56VYLaiwSejfyYeRFMITEn/UxGYhcXZ5xMUUCG0TxjBhLYpTBuwIDAQABAoIBAA4dwebcxkrH99Poa8+WkiE7JgaS9sahx9OBI2xwJANoIU2TpzGuNLQZ76uLgB+rPWZTD9Xm5a1iJjwOyQ9/937TzPCk91D0tpgcusRikb8jx/6TGB9acL4kBjYUVCCHr3BA2G75MKKGtJ2OMvAbCQSosZj+r2VDwYFEPUkV2jheE5JHSBkwyIRrus3JCwu8gu5fyCg9z8ljcxJxI5HIsi4v8Z21aCw/cLj7h5cMt44wCjQz4rOfYNBEFeHDtlfR1QtWKgjm4ZHHJbKrzf9b2kQXclziceEbSM0tYbROEXKi+s0Zc+z3HEG89vv0vfN400clmzzIAijKY6gg3pPRWdECgYEA+lnWYbSlXDMNYx6RBXm1RnlMUYIm4oy4/9ljgnoGJ6WCn3SjFkgaDtiKfGIG1BSB85r04pAPANgcWHf5tWDnq0ARvBVG0BX2bKd++7B3D4d3CRYKCwm88SslJXv9dfHVhq4+zViFPiUWwT20A72jCuUCvL88y5fh/YBecfdh+jECgYEA4r5RD0NB9dMaeg5/jk/GEHIo4Z9KLc6FrSoOFo2xFkPOy1sgDpDOiNtypuWvniO7k7Ose3DS3hlfTMsKzIW/CgQJ20+Y4cvBWDaOsRxfjj7w3d+jH5OSJdKKSzTrgLKc9ZhlRzVXy0J0hipIA6HG5kdVdLXmh85ITmf1CbJhE6sCgYBjPVeBNbXTHZ2x6/z62aslO5IoQVqetb/kE82hfDOSZcao5Ph9Lam+ttH2ynkAevykj4mBgi+gWwqpey2uW7KaLPSaxShj9kDQA3mP1fzsV/u0y1rB02Nlin/YIxVvOqU1FT9p8SwoXVVu1sHUNck62VtDbN9xqUx5S/ikXrclEQKBgQCoTssOwEcK+Vty9KYcdfy4onTUHZBLdjxl8Iyqkxy7QTQUYRznkvesQPDXEDGO+kk3dyx2KKZt9Hl4IFNww2quPZcvcuMx4DQxjbXXpA8OIIxcta95NepLJwA+mRai3nKCH1A2TlNP7pFeMa5o+8IPly3Ix2lKr4Wepa4PN5i1pwKBgCZ1QP6XAOERl9NznNmU0rXVcvYNP4PIIfQWfvGsldZ4QKkmjjAGiS0/oYqdWs+UDRZyCRChaVjDXO9fk0PEG5OGKAj9nyiYCT/M8xtJ3UeP5ffZZvJ/vnye3QdDIo1e38ZzsWwJHmLYw7fRqY9W5Vxo0Vsy22U3CJY70KTxVdTy" ],
+        "keyUse" : [ "ENC" ],
+        "certificate" : [ "MIICmzCCAYMCBgGG3GWycDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdvW00t9yMUtTD1RKFUM1/5kPyxCpvhgFohdxIlNf/MNBQ+IKnXvoEEYjezhWmUMCuc5b4u6ajOYqiB6gcScYQgNbyi+Arm4xT21IDanSqoYFQw51HxgBNMh8Il7uoaKK4ZrdZnLexAszvRK5HPHTZhTyC9Ry+XYmzgrKoDOI5Rf/DdZrn0f2OMY1RdjKpo9Ymnvd4fNtVhbJLPIsbbaZWVxS0HHmyIvlPb9e7Sn3PiXv9tqnDCY5WLVmtweNsQDB0YSJIwYj+5aoAX6vUFGmYktHsc1dwvd4rj/npVgtqLBJ6N/Jh5EUwhMSf9TEZiFxdnnExRQIbRPGMGEtilMG7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK3kQ1VkQrzvSWvmXmazmNoA1ZiPzRDs1XhGUWxgsxzgPylr3dGBuqQbKvgnLUBQLSqlJHpI4fZflHswu1qrvVZYtekPcGef4WhcKAu2i1RwxrKa6RJQ1tRbrLuVYCzPv5p/DWgltWVn88aoLnqQn0SK/0PB/o4a4Cm7Kq2ZzCr1dACBr06LvOHsc7249OySmbG4HH+pLK6jVURhZ9VaObqAHe2FJBVVoIzURbdiRRURqumrIvbnpeaU1aFyg6ED5wTnXvmMPmVPt9F79mcB33bASO5wyu00X8t1hyN2Show2l2vxLACGUzVkTQt15s7uDLKE7qLmKSR3EuSGXWv3wA=" ],
+        "priority" : [ "100" ],
+        "algorithm" : [ "RSA-OAEP" ]
+      }
+    }, {
+      "id" : "230cb681-9ceb-4b1b-8a4c-929a11b08de0",
+      "name" : "hmac-generated-hs512",
+      "providerId" : "hmac-generated",
+      "subComponents" : { },
+      "config" : {
+        "kid" : [ "8a489935-9a95-459b-9059-59b438ef0fa8" ],
+        "secret" : [ "xSCVgBlrLPWoF54gKQdR7BqXlfNaCD43xtS_ZgQRC0tGNAbqhy2Q9y8LdD2IR7K__8VGaDGYtyZayopgTebhDBb4gHDjDOBX7flhFYRrm0G3aTIuCIyFG-bPULwmyP_oHeC6tjwdQhqx5G0tE2mQQqPC9dDZuUA5I7QREIGK8cI" ],
+        "priority" : [ "100" ],
+        "algorithm" : [ "HS512" ]
+      }
+    }, {
       "id" : "28ca0b6d-b2e2-4785-b04b-2391e6344e30",
       "name" : "aes-generated",
       "providerId" : "aes-generated",
@@ -2175,23 +2212,11 @@
       "providerId" : "hmac-generated",
       "subComponents" : { },
       "config" : {
-        "kid" : [ "c8500166-5cc4-4085-ad0f-853c3b0b0233" ],
-        "secret" : [ "TI3xg__G2Qy8C47DracpYir2X4ItQZSrhgr5KSlwRNISDbBqZ-ky3OcAyokSXMcpweSOaCPvbivpvzJNklUBvw" ],
+        "kid" : [ "5034d264-cb50-4006-a59e-2ce636eb5f38" ],
+        "secret" : [ "ToVIw-a4IE-Yp9JpP8ztb8NAICYO8CT3tUiDPT6DdiBcgzKJ9Ym9vspxGVdmPceX3mAgbnGLAcTx1PkInSVrbZs-tX9QXFwdlyGbewhKiNpH8wEg32Wk4GuUDpTv8JCsymgWyQBY681jvIMv05eCoK2QWpqCzcgP828KM5peCzo" ],
         "priority" : [ "100" ],
         "algorithm" : [ "HS256" ]
       }
-    }, {
-      "id" : "2f53ccf3-37b0-4d34-83e7-ed497499ee51",
-      "name" : "rsa-enc-generated",
-      "providerId" : "rsa-enc-generated",
-      "subComponents" : { },
-      "config" : {
-        "privateKey" : [ "MIIEowIBAAKCAQEA3b1tNLfcjFLUw9UShVDNf+ZD8sQqb4YBaIXcSJTX/zDQUPiCp176BBGI3s4VplDArnOW+LumozmKogeoHEnGEIDW8ovgK5uMU9tSA2p0qqGBUMOdR8YATTIfCJe7qGiiuGa3WZy3sQLM70SuRzx02YU8gvUcvl2Js4KyqAziOUX/w3Wa59H9jjGNUXYyqaPWJp73eHzbVYWySzyLG22mVlcUtBx5siL5T2/Xu0p9z4l7/bapwwmOVi1ZrcHjbEAwdGEiSMGI/uWqAF+r1BRpmJLR7HNXcL3eK4/56VYLaiwSejfyYeRFMITEn/UxGYhcXZ5xMUUCG0TxjBhLYpTBuwIDAQABAoIBAA4dwebcxkrH99Poa8+WkiE7JgaS9sahx9OBI2xwJANoIU2TpzGuNLQZ76uLgB+rPWZTD9Xm5a1iJjwOyQ9/937TzPCk91D0tpgcusRikb8jx/6TGB9acL4kBjYUVCCHr3BA2G75MKKGtJ2OMvAbCQSosZj+r2VDwYFEPUkV2jheE5JHSBkwyIRrus3JCwu8gu5fyCg9z8ljcxJxI5HIsi4v8Z21aCw/cLj7h5cMt44wCjQz4rOfYNBEFeHDtlfR1QtWKgjm4ZHHJbKrzf9b2kQXclziceEbSM0tYbROEXKi+s0Zc+z3HEG89vv0vfN400clmzzIAijKY6gg3pPRWdECgYEA+lnWYbSlXDMNYx6RBXm1RnlMUYIm4oy4/9ljgnoGJ6WCn3SjFkgaDtiKfGIG1BSB85r04pAPANgcWHf5tWDnq0ARvBVG0BX2bKd++7B3D4d3CRYKCwm88SslJXv9dfHVhq4+zViFPiUWwT20A72jCuUCvL88y5fh/YBecfdh+jECgYEA4r5RD0NB9dMaeg5/jk/GEHIo4Z9KLc6FrSoOFo2xFkPOy1sgDpDOiNtypuWvniO7k7Ose3DS3hlfTMsKzIW/CgQJ20+Y4cvBWDaOsRxfjj7w3d+jH5OSJdKKSzTrgLKc9ZhlRzVXy0J0hipIA6HG5kdVdLXmh85ITmf1CbJhE6sCgYBjPVeBNbXTHZ2x6/z62aslO5IoQVqetb/kE82hfDOSZcao5Ph9Lam+ttH2ynkAevykj4mBgi+gWwqpey2uW7KaLPSaxShj9kDQA3mP1fzsV/u0y1rB02Nlin/YIxVvOqU1FT9p8SwoXVVu1sHUNck62VtDbN9xqUx5S/ikXrclEQKBgQCoTssOwEcK+Vty9KYcdfy4onTUHZBLdjxl8Iyqkxy7QTQUYRznkvesQPDXEDGO+kk3dyx2KKZt9Hl4IFNww2quPZcvcuMx4DQxjbXXpA8OIIxcta95NepLJwA+mRai3nKCH1A2TlNP7pFeMa5o+8IPly3Ix2lKr4Wepa4PN5i1pwKBgCZ1QP6XAOERl9NznNmU0rXVcvYNP4PIIfQWfvGsldZ4QKkmjjAGiS0/oYqdWs+UDRZyCRChaVjDXO9fk0PEG5OGKAj9nyiYCT/M8xtJ3UeP5ffZZvJ/vnye3QdDIo1e38ZzsWwJHmLYw7fRqY9W5Vxo0Vsy22U3CJY70KTxVdTy" ],
-        "keyUse" : [ "ENC" ],
-        "certificate" : [ "MIICmzCCAYMCBgGG3GWycDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdvW00t9yMUtTD1RKFUM1/5kPyxCpvhgFohdxIlNf/MNBQ+IKnXvoEEYjezhWmUMCuc5b4u6ajOYqiB6gcScYQgNbyi+Arm4xT21IDanSqoYFQw51HxgBNMh8Il7uoaKK4ZrdZnLexAszvRK5HPHTZhTyC9Ry+XYmzgrKoDOI5Rf/DdZrn0f2OMY1RdjKpo9Ymnvd4fNtVhbJLPIsbbaZWVxS0HHmyIvlPb9e7Sn3PiXv9tqnDCY5WLVmtweNsQDB0YSJIwYj+5aoAX6vUFGmYktHsc1dwvd4rj/npVgtqLBJ6N/Jh5EUwhMSf9TEZiFxdnnExRQIbRPGMGEtilMG7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK3kQ1VkQrzvSWvmXmazmNoA1ZiPzRDs1XhGUWxgsxzgPylr3dGBuqQbKvgnLUBQLSqlJHpI4fZflHswu1qrvVZYtekPcGef4WhcKAu2i1RwxrKa6RJQ1tRbrLuVYCzPv5p/DWgltWVn88aoLnqQn0SK/0PB/o4a4Cm7Kq2ZzCr1dACBr06LvOHsc7249OySmbG4HH+pLK6jVURhZ9VaObqAHe2FJBVVoIzURbdiRRURqumrIvbnpeaU1aFyg6ED5wTnXvmMPmVPt9F79mcB33bASO5wyu00X8t1hyN2Show2l2vxLACGUzVkTQt15s7uDLKE7qLmKSR3EuSGXWv3wA=" ],
-        "priority" : [ "100" ],
-        "algorithm" : [ "RSA-OAEP" ]
-      }
     }, {
       "id" : "2293ff99-3c6d-46d1-8635-5e679d5b134a",
       "name" : "rsa-generated",
@@ -2229,35 +2254,6 @@
       "flowAlias" : "Verify Existing Account by Re-authentication",
       "userSetupAllowed" : false
     } ]
-  }, {
-    "id" : "bc0b483f-4a3f-4c15-bf65-b26f5320e6c9",
-    "alias" : "Authentication Options",
-    "description" : "Authentication options.",
-    "providerId" : "basic-flow",
-    "topLevel" : false,
-    "builtIn" : true,
-    "authenticationExecutions" : [ {
-      "authenticator" : "basic-auth",
-      "authenticatorFlow" : false,
-      "requirement" : "REQUIRED",
-      "priority" : 10,
-      "autheticatorFlow" : false,
-      "userSetupAllowed" : false
-    }, {
-      "authenticator" : "basic-auth-otp",
-      "authenticatorFlow" : false,
-      "requirement" : "DISABLED",
-      "priority" : 20,
-      "autheticatorFlow" : false,
-      "userSetupAllowed" : false
-    }, {
-      "authenticator" : "auth-spnego",
-      "authenticatorFlow" : false,
-      "requirement" : "DISABLED",
-      "priority" : 30,
-      "autheticatorFlow" : false,
-      "userSetupAllowed" : false
-    } ]
   }, {
     "id" : "a690c715-fbae-4c20-b680-bd4010718761",
     "alias" : "Browser - Conditional OTP",
@@ -2574,28 +2570,6 @@
       "flowAlias" : "Browser - Conditional OTP",
       "userSetupAllowed" : false
     } ]
-  }, {
-    "id" : "b8f1f963-6813-4875-bae8-ce48a813763b",
-    "alias" : "http challenge",
-    "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes",
-    "providerId" : "basic-flow",
-    "topLevel" : true,
-    "builtIn" : true,
-    "authenticationExecutions" : [ {
-      "authenticator" : "no-cookie-redirect",
-      "authenticatorFlow" : false,
-      "requirement" : "REQUIRED",
-      "priority" : 10,
-      "autheticatorFlow" : false,
-      "userSetupAllowed" : false
-    }, {
-      "authenticatorFlow" : true,
-      "requirement" : "REQUIRED",
-      "priority" : 20,
-      "autheticatorFlow" : true,
-      "flowAlias" : "Authentication Options",
-      "userSetupAllowed" : false
-    } ]
   }, {
     "id" : "4c983c77-241f-41c5-8b8a-e2cd6fc08914",
     "alias" : "registration",
@@ -2626,13 +2600,6 @@
       "priority" : 20,
       "autheticatorFlow" : false,
       "userSetupAllowed" : false
-    }, {
-      "authenticator" : "registration-profile-action",
-      "authenticatorFlow" : false,
-      "requirement" : "REQUIRED",
-      "priority" : 40,
-      "autheticatorFlow" : false,
-      "userSetupAllowed" : false
     }, {
       "authenticator" : "registration-password-action",
       "authenticatorFlow" : false,
@@ -2777,6 +2744,14 @@
     "defaultAction" : false,
     "priority" : 80,
     "config" : { }
+  }, {
+    "alias" : "delete_credential",
+    "name" : "Delete Credential",
+    "providerId" : "delete_credential",
+    "enabled" : true,
+    "defaultAction" : false,
+    "priority" : 100,
+    "config" : { }
   }, {
     "alias" : "update_user_locale",
     "name" : "Update User Locale",
@@ -2792,6 +2767,7 @@
   "resetCredentialsFlow" : "reset credentials",
   "clientAuthenticationFlow" : "clients",
   "dockerAuthenticationFlow" : "docker auth",
+  "firstBrokerLoginFlow" : "first broker login",
   "attributes" : {
     "cibaBackchannelTokenDeliveryMode" : "poll",
     "cibaAuthRequestedUserHint" : "login_hint",
@@ -2811,7 +2787,7 @@
     "clientSessionMaxLifespan" : "0",
     "shortVerificationUri" : ""
   },
-  "keycloakVersion" : "21.0.2",
+  "keycloakVersion" : "24.0.5",
   "userManagedAccessAllowed" : false,
   "clientProfiles" : {
     "profiles" : [ ]
diff --git a/dbrepo-metadata-service/rest-service/src/test/resources/init/weather_at.sql b/dbrepo-metadata-service/rest-service/src/test/resources/init/weather_at.sql
new file mode 100644
index 0000000000000000000000000000000000000000..d02e1945506e5d66bb17dd10582f81814b940ad1
--- /dev/null
+++ b/dbrepo-metadata-service/rest-service/src/test/resources/init/weather_at.sql
@@ -0,0 +1,11 @@
+CREATE
+DATABASE weather_at;
+USE
+weather_at;
+
+CREATE TABLE weather_location
+(
+    location VARCHAR(255) PRIMARY KEY,
+    lat      DOUBLE PRECISION NULL,
+    lng      DOUBLE PRECISION NULL
+) WITH SYSTEM VERSIONING COMMENT 'Weather location';
\ No newline at end of file
diff --git a/dbrepo-metadata-service/services/pom.xml b/dbrepo-metadata-service/services/pom.xml
index bb101e9af6539a35e1c761f708392057e48afeea..2961f680d12220dbfbf6c91a7cf64797ff213bf3 100644
--- a/dbrepo-metadata-service/services/pom.xml
+++ b/dbrepo-metadata-service/services/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <artifactId>dbrepo-metadata-service</artifactId>
         <groupId>at.tuwien</groupId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-services</artifactId>
     <name>dbrepo-metadata-service-services</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java
index 35e55797ebffb688f801bfcf64163d3a4a630049..35dd5fe2b6b4f54b882d20c5685c57847dfc2c1d 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java
@@ -73,6 +73,7 @@ public class AuthTokenFilter extends OncePerRequestFilter {
         final JWTVerifier verifier = verification.build();
         final DecodedJWT jwt = verifier.verify(token);
         final RealmAccessDto realmAccess = jwt.getClaim("realm_access").as(RealmAccessDto.class);
+        log.trace("token contains uid: {}", jwt.getClaim("uid").asString());
         return UserDetailsDto.builder()
                 .id(jwt.getClaim("uid").asString())
                 .username(jwt.getClaim("preferred_username").asString())
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/BasicAuthenticationProvider.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/BasicAuthenticationProvider.java
index 80fdd15f1daff7625508eb8d46d9b54332a5150f..b9fea2b54b1c242f58dbc0cd578e9dff328ba49c 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/BasicAuthenticationProvider.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/BasicAuthenticationProvider.java
@@ -32,6 +32,7 @@ public class BasicAuthenticationProvider implements AuthenticationManager {
         try {
             final TokenDto tokenDto = keycloakGateway.obtainUserToken(auth.getName(), auth.getCredentials().toString());
             final UserDetails userDetails = authTokenFilter.verifyJwt(tokenDto.getAccessToken());
+            log.debug("set basic auth principal: {}", userDetails);
             return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
         } catch (ServletException | CredentialsInvalidException | AccountNotSetupException |
                  AuthServiceConnectionException e) {
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java
index d7c036049b84cfe9d028fa60fa532aa6ba00dc75..3000ece11e91cc2b58f2edcfac85f6a0f8a60528 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java
@@ -21,20 +21,31 @@ public interface DatabaseService {
      */
     List<Database> findAll();
 
+    List<Database> findAllPublicOrSchemaPublic();
+
+    List<Database> findAllPublicOrSchemaPublicOrReadAccessByInternalName(UUID userId, String internalName);
+
+    /**
+     * Finds all databases stored in the metadata database.
+     *
+     * @param userId The user id.
+     * @return List of databases.
+     */
+    List<Database> findAllAtLestReadAccess(UUID userId);
+
     /**
      * Finds all databases stored in the metadata database.
      *
      * @param userId The user id.
      * @return List of databases.
      */
-    List<Database> findAllAccess(UUID userId);
+    List<Database> findAllPublicOrSchemaPublicOrReadAccess(UUID userId);
 
     /**
      * @param internalName The database internal name.
-     * @return The database if found.
-     * @throws DatabaseNotFoundException The database was not found.
+     * @return The databases if found.
      */
-    Database findByInternalName(String internalName) throws DatabaseNotFoundException;
+    List<Database> findAllPublicOrSchemaPublicByInternalName(String internalName);
 
     /**
      * Find a database by id, only used in the authentication service
@@ -51,12 +62,13 @@ public interface DatabaseService {
      * @param container The container.
      * @param createDto The metadata.
      * @param user      The user.
+     * @param internalUsers      The list of internal users.
      * @return The database, if successful.
      * @throws UserNotFoundException          If the container/user was not found in the metadata database.
      * @throws DataServiceException           If the data service returned non-successfully.
      * @throws DataServiceConnectionException If failing to connect to the data service/search service.
      */
-    Database create(Container container, DatabaseCreateDto createDto, User user) throws UserNotFoundException,
+    Database create(Container container, DatabaseCreateDto createDto, User user, List<User> internalUsers) throws UserNotFoundException,
             ContainerNotFoundException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException,
             SearchServiceException, SearchServiceConnectionException;
 
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java
index 8e7c715ad06d434bfdb947136546f686e91835ad..6416da9b803d93f633f13223127a978954dd6e5d 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java
@@ -1,9 +1,12 @@
 package at.tuwien.service;
 
 import at.tuwien.api.auth.SignupRequestDto;
-import at.tuwien.api.user.*;
+import at.tuwien.api.user.UserPasswordDto;
+import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.entities.user.User;
-import at.tuwien.exception.*;
+import at.tuwien.exception.EmailExistsException;
+import at.tuwien.exception.UserExistsException;
+import at.tuwien.exception.UserNotFoundException;
 
 import java.util.List;
 import java.util.UUID;
@@ -26,6 +29,8 @@ public interface UserService {
      */
     User findByUsername(String username) throws UserNotFoundException;
 
+    List<User> findAllInternalUsers();
+
     /**
      * Finds a specific user in the metadata database by given id.
      *
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java
index a0f45fb34f3c3762a8da630c622d9aa23d327af3..1c302c206871234da50f2cc9de234d7c94a84d66 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java
@@ -51,7 +51,7 @@ public class AccessServiceImpl implements AccessService {
     public DatabaseAccess find(Database database, User user) throws AccessNotFoundException {
         final Optional<DatabaseAccess> optional = database.getAccesses()
                 .stream()
-                .filter(a -> a.getUser().getUsername().equals(user.getUsername()))
+                .filter(a -> a.getHuserid().equals(user.getId()))
                 .findFirst();
         if (optional.isEmpty()) {
             log.error("Failed to find database access for database with id: {}", database.getId());
@@ -94,7 +94,7 @@ public class AccessServiceImpl implements AccessService {
         /* update in metadata database */
         final Optional<DatabaseAccess> optional = database.getAccesses()
                 .stream()
-                .filter(a -> a.getUser().getId().equals(user.getId()))
+                .filter(a -> a.getHuserid().equals(user.getId()))
                 .findFirst();
         if (optional.isEmpty()) {
             log.error("Failed to update access for user with id: {}", user.getId());
@@ -116,7 +116,8 @@ public class AccessServiceImpl implements AccessService {
         /* delete in data database */
         dataServiceGateway.deleteAccess(database.getId(), user.getId());
         /* delete in metadata database */
-        database.getAccesses().remove(find(database, user));
+        database.getAccesses()
+                .remove(find(database, user));
         databaseRepository.save(database);
         /* update in search service */
         searchServiceGateway.update(databaseService.findById(database.getId()));
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java
index f3f4cccc5d5d8742d4c35d12b982001e0cfbb431..7415ded56a8a2a926742ef3b44b025f7fb535baa 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java
@@ -8,7 +8,9 @@ import at.tuwien.api.database.internal.CreateDatabaseDto;
 import at.tuwien.api.database.table.TableDto;
 import at.tuwien.api.user.internal.UpdateUserPasswordDto;
 import at.tuwien.entities.container.Container;
-import at.tuwien.entities.database.*;
+import at.tuwien.entities.database.Database;
+import at.tuwien.entities.database.View;
+import at.tuwien.entities.database.ViewColumn;
 import at.tuwien.entities.database.table.Table;
 import at.tuwien.entities.database.table.columns.TableColumn;
 import at.tuwien.entities.database.table.constraints.foreignKey.ForeignKey;
@@ -57,19 +59,28 @@ public class DatabaseServiceImpl implements DatabaseService {
     }
 
     @Override
-    public List<Database> findAllAccess(UUID userId) {
-        return databaseRepository.findReadAccess(userId);
+    public List<Database> findAllPublicOrSchemaPublic() {
+        return databaseRepository.findAllPublicOrSchemaPublicDesc();
     }
 
     @Override
-    public Database findByInternalName(String internalName) throws DatabaseNotFoundException {
-        log.trace("find database by internal name: {}", internalName);
-        final Optional<Database> database = databaseRepository.findByInternalName(internalName);
-        if (database.isEmpty()) {
-            log.error("Failed to find database with internal name {} in metadata database", internalName);
-            throw new DatabaseNotFoundException("Failed to find database in metadata database");
-        }
-        return database.get();
+    public List<Database> findAllPublicOrSchemaPublicOrReadAccessByInternalName(UUID userId, String internalName) {
+        return databaseRepository.findAllPublicOrSchemaPublicOrReadAccessByInternalNameDesc(userId, internalName);
+    }
+
+    @Override
+    public List<Database> findAllAtLestReadAccess(UUID userId) {
+        return databaseRepository.findAllAtLestReadAccessDesc(userId);
+    }
+
+    @Override
+    public List<Database> findAllPublicOrSchemaPublicOrReadAccess(UUID userId) {
+        return databaseRepository.findAllPublicOrSchemaPublicOrReadAccessDesc(userId);
+    }
+
+    @Override
+    public List<Database> findAllPublicOrSchemaPublicByInternalName(String internalName) {
+        return databaseRepository.findAllPublicOrSchemaPublicByInternalNameDesc(internalName);
     }
 
     @Override
@@ -85,9 +96,9 @@ public class DatabaseServiceImpl implements DatabaseService {
 
     @Override
     @Transactional
-    public Database create(Container container, DatabaseCreateDto data, User user) throws UserNotFoundException,
-            ContainerNotFoundException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException,
-            SearchServiceException, SearchServiceConnectionException {
+    public Database create(Container container, DatabaseCreateDto data, User user, List<User> internalUsers)
+            throws UserNotFoundException, ContainerNotFoundException, DataServiceException, SearchServiceException,
+            DataServiceConnectionException, DatabaseNotFoundException, SearchServiceConnectionException {
         final Database entity = Database.builder()
                 .isPublic(data.getIsPublic())
                 .isSchemaPublic(data.getIsSchemaPublic())
@@ -119,13 +130,11 @@ public class DatabaseServiceImpl implements DatabaseService {
         /* create in metadata database */
         final Database entity1 = databaseRepository.save(entity);
         entity1.getAccesses()
-                .add(DatabaseAccess.builder()
-                        .type(AccessType.WRITE_ALL)
-                        .hdbid(entity1.getId())
-                        .database(entity1)
-                        .huserid(user.getId())
-                        .user(user)
-                        .build());
+                .add(metadataMapper.userToWriteAllAccess(entity1, user));
+        entity1.getAccesses()
+                .addAll(internalUsers.stream()
+                        .map(internalUser -> metadataMapper.userToWriteAllAccess(entity1, internalUser))
+                        .toList());
         final Database database = databaseRepository.save(entity1);
         /* create in search service */
         searchServiceGateway.update(database);
@@ -137,7 +146,7 @@ public class DatabaseServiceImpl implements DatabaseService {
     @Transactional(readOnly = true)
     public void updatePassword(Database database, User user) throws DataServiceException, DataServiceConnectionException,
             DatabaseNotFoundException {
-        final List<Database> databases = databaseRepository.findReadAccess(user.getId())
+        final List<Database> databases = databaseRepository.findAllAtLestReadAccessDesc(user.getId())
                 .stream()
                 .distinct()
                 .toList();
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
index 8706e18bbc7719b7d8beec8c36a33834b4397471..39e4824706a6d89a3588c6748c46ee2c3935b63e 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
@@ -187,41 +187,41 @@ public class IdentifierServiceImpl implements IdentifierService {
             identifier.setCreators(new LinkedList<>(data.getCreators()
                     .stream()
                     .map(metadataMapper::creatorCreateDtoToCreator)
-                    .peek(c -> c.setIdentifier(identifier))
                     .toList()));
-            log.debug("set {} creator(s)", identifier.getCreators().size());
+            identifier.getCreators()
+                    .forEach(c -> c.setIdentifier(identifier));
         }
         if (data.getRelatedIdentifiers() != null) {
             identifier.setRelatedIdentifiers(new LinkedList<>(data.getRelatedIdentifiers()
                     .stream()
                     .map(metadataMapper::relatedIdentifierCreateDtoToRelatedIdentifier)
-                    .peek(r -> r.setIdentifier(identifier))
                     .toList()));
-            log.debug("set {} related identifier(s)", identifier.getRelatedIdentifiers().size());
+            identifier.getRelatedIdentifiers()
+                    .forEach(r -> r.setIdentifier(identifier));
         }
         if (data.getTitles() != null) {
             identifier.setTitles(new LinkedList<>(data.getTitles()
                     .stream()
                     .map(metadataMapper::identifierCreateTitleDtoToIdentifierTitle)
-                    .peek(t -> t.setIdentifier(identifier))
                     .toList()));
-            log.debug("set {} title(s)", identifier.getTitles().size());
+            identifier.getTitles()
+                    .forEach(t -> t.setIdentifier(identifier));
         }
         if (data.getDescriptions() != null) {
             identifier.setDescriptions(new LinkedList<>(data.getDescriptions()
                     .stream()
                     .map(metadataMapper::identifierCreateDescriptionDtoToIdentifierDescription)
-                    .peek(d -> d.setIdentifier(identifier))
                     .toList()));
-            log.debug("set {} description(s)", identifier.getDescriptions().size());
+            identifier.getDescriptions()
+                    .forEach(d -> d.setIdentifier(identifier));
         }
         if (data.getFunders() != null) {
             identifier.setFunders(new LinkedList<>(data.getFunders()
                     .stream()
                     .map(metadataMapper::identifierFunderSaveDtoToIdentifierFunder)
-                    .peek(d -> d.setIdentifier(identifier))
                     .toList()));
-            log.debug("set {} funder(s)", identifier.getFunders().size());
+            identifier.getFunders()
+                    .forEach(f -> f.setIdentifier(identifier));
         }
         return save(identifier);
     }
@@ -238,45 +238,44 @@ public class IdentifierServiceImpl implements IdentifierService {
         identifier.setStatus(IdentifierStatusType.DRAFT);
         /* create in metadata database */
         if (data.getCreators() != null) {
-            identifier.setCreators(new LinkedList<>(data.getCreators()
+            identifier.setCreators(data.getCreators()
                     .stream()
                     .map(metadataMapper::creatorCreateDtoToCreator)
-                    .peek(c -> c.setIdentifier(identifier))
-                    .toList()));
-            log.debug("set {} creator(s)", identifier.getCreators().size());
+                    .toList());
+            identifier.getCreators()
+                    .forEach(c -> c.setIdentifier(identifier));
         }
         if (data.getRelatedIdentifiers() != null) {
-            identifier.setRelatedIdentifiers(new LinkedList<>(data.getRelatedIdentifiers()
+            identifier.setRelatedIdentifiers(data.getRelatedIdentifiers()
                     .stream()
                     .map(metadataMapper::relatedIdentifierCreateDtoToRelatedIdentifier)
-                    .peek(r -> r.setIdentifier(identifier))
-                    .toList()));
-            log.debug("set {} related identifier(s)", identifier.getRelatedIdentifiers().size());
+                    .toList());
+            identifier.getRelatedIdentifiers()
+                    .forEach(r -> r.setIdentifier(identifier));
         }
         if (data.getTitles() != null) {
-            identifier.setTitles(null);
-            identifier.setTitles(new LinkedList<>(data.getTitles()
+            identifier.setTitles(data.getTitles()
                     .stream()
                     .map(metadataMapper::identifierCreateTitleDtoToIdentifierTitle)
-                    .peek(t -> t.setIdentifier(identifier))
-                    .toList()));
-            log.debug("set {} title(s)", identifier.getTitles().size());
+                    .toList());
+            identifier.getTitles()
+                    .forEach(t -> t.setIdentifier(identifier));
         }
         if (data.getDescriptions() != null) {
-            identifier.setDescriptions(new LinkedList<>(data.getDescriptions()
+            identifier.setDescriptions(data.getDescriptions()
                     .stream()
                     .map(metadataMapper::identifierCreateDescriptionDtoToIdentifierDescription)
-                    .peek(d -> d.setIdentifier(identifier))
-                    .toList()));
-            log.debug("set {} description(s)", identifier.getDescriptions().size());
+                    .toList());
+            identifier.getDescriptions()
+                    .forEach(d -> d.setIdentifier(identifier));
         }
         if (data.getFunders() != null) {
-            identifier.setFunders(new LinkedList<>(data.getFunders()
+            identifier.setFunders(data.getFunders()
                     .stream()
                     .map(metadataMapper::identifierFunderSaveDtoToIdentifierFunder)
-                    .peek(d -> d.setIdentifier(identifier))
-                    .toList()));
-            log.debug("set {} funder(s)", identifier.getFunders().size());
+                    .toList());
+            identifier.getFunders()
+                    .forEach(f -> f.setIdentifier(identifier));
         }
         return save(identifier);
     }
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
index 7e07b68d1c965ed42261ab508273770be50a6454..6f9f43aeda934ecd7065ead11077ad529597e118 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
@@ -1,9 +1,13 @@
 package at.tuwien.service.impl;
 
 import at.tuwien.api.auth.SignupRequestDto;
-import at.tuwien.api.user.*;
+import at.tuwien.api.user.UserPasswordDto;
+import at.tuwien.api.user.UserUpdateDto;
+import at.tuwien.config.KeycloakConfig;
 import at.tuwien.entities.user.User;
-import at.tuwien.exception.*;
+import at.tuwien.exception.EmailExistsException;
+import at.tuwien.exception.UserExistsException;
+import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.repository.UserRepository;
 import at.tuwien.service.UserService;
 import lombok.extern.log4j.Log4j2;
@@ -20,10 +24,12 @@ import java.util.UUID;
 @Service
 public class UserServiceImpl implements UserService {
 
+    private final KeycloakConfig keycloakConfig;
     private final UserRepository userRepository;
 
     @Autowired
-    public UserServiceImpl(UserRepository userRepository) {
+    public UserServiceImpl(KeycloakConfig keycloakConfig, UserRepository userRepository) {
+        this.keycloakConfig = keycloakConfig;
         this.userRepository = userRepository;
     }
 
@@ -36,18 +42,23 @@ public class UserServiceImpl implements UserService {
     public User findByUsername(String username) throws UserNotFoundException {
         final Optional<User> optional = userRepository.findByUsername(username);
         if (optional.isEmpty()) {
-            log.error("Failed to find user with username {}", username);
-            throw new UserNotFoundException("Failed to find user with username " + username);
+            log.error("Failed to find user with username: {}", username);
+            throw new UserNotFoundException("Failed to find user with username: " + username);
         }
         return optional.get();
     }
 
+    @Override
+    public List<User> findAllInternalUsers() {
+        return userRepository.findAllInternal();
+    }
+
     @Override
     public User findById(UUID id) throws UserNotFoundException {
         final Optional<User> optional = userRepository.findById(id);
         if (optional.isEmpty()) {
-            log.error("Failed to find user with id {}", id);
-            throw new UserNotFoundException("Failed to find user with id " + id);
+            log.error("Failed to find user with id: {}", id);
+            throw new UserNotFoundException("Failed to find user with id: " + id);
         }
         return optional.get();
     }
@@ -62,10 +73,11 @@ public class UserServiceImpl implements UserService {
                 .theme("light")
                 .mariadbPassword(getMariaDbPassword(data.getPassword()))
                 .language("en")
+                .isInternal(false)
                 .build();
         /* create at metadata database */
         final User user = userRepository.save(entity);
-        log.info("Created user with id {}", user.getId());
+        log.info("Created user with id: {}", user.getId());
         return user;
     }
 
@@ -79,7 +91,7 @@ public class UserServiceImpl implements UserService {
         user.setLanguage(data.getLanguage());
         /* create at metadata database */
         user = userRepository.save(user);
-        log.info("Modified user with id {}", user.getId());
+        log.info("Modified user with id: {}", user.getId());
         return user;
     }
 
@@ -88,7 +100,7 @@ public class UserServiceImpl implements UserService {
         user.setMariadbPassword(getMariaDbPassword(data.getPassword()));
         /* update at metadata database */
         userRepository.save(user);
-        log.info("Updated password of user with id {}", user.getId());
+        log.info("Updated password of user with id: {}", user.getId());
     }
 
     @Override
diff --git a/dbrepo-metadata-service/test/pom.xml b/dbrepo-metadata-service/test/pom.xml
index cdb1783c840fcf2e088e5a7d8f29bc67a3d5693d..c58104714adaebc14cfb1c3b5e268e807d8c8e29 100644
--- a/dbrepo-metadata-service/test/pom.xml
+++ b/dbrepo-metadata-service/test/pom.xml
@@ -6,12 +6,12 @@
     <parent>
         <groupId>at.tuwien</groupId>
         <artifactId>dbrepo-metadata-service</artifactId>
-        <version>1.6.0</version>
+        <version>1.6.1</version>
     </parent>
 
     <artifactId>dbrepo-metadata-service-test</artifactId>
     <name>dbrepo-metadata-service-test</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
     <dependencies>
         <dependency>
diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java
index 887c61501b3b4c40bfaebcfb11f8b9fc95be6adb..91936adaf8f27c87dbd4019b4ee6ce8a67e118a5 100644
--- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java
+++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java
@@ -11,6 +11,7 @@ import java.util.List;
 public abstract class AbstractUnitTest extends BaseTest {
 
     public void genesis() {
+        IMAGE_1_DTO.setOperators(IMAGE_1_OPERATORS_DTO);
         CONTAINER_1_PRIVILEGED_DTO.setImage(IMAGE_1_DTO);
         IMAGE_1.setOperators(new LinkedList<>(IMAGE_1_OPERATORS));
         CONTAINER_1.setDatabases(new LinkedList<>(List.of(DATABASE_1, DATABASE_2, DATABASE_3)));
@@ -31,7 +32,8 @@ public abstract class AbstractUnitTest extends BaseTest {
         TABLE_1_COLUMNS.get(0).setConcept(null);
         DATABASE_1.setOwner(USER_1);
         DATABASE_1.setSubsets(new LinkedList<>());
-        DATABASE_1.setIsPublic(false);
+        DATABASE_1.setIsPublic(DATABASE_1_PUBLIC);
+        DATABASE_1.setIsSchemaPublic(DATABASE_1_SCHEMA_PUBLIC);
         DATABASE_1_USER_1_READ_ACCESS.setType(AccessType.READ);
         DATABASE_1.setAccesses(new LinkedList<>(List.of(DATABASE_1_USER_1_READ_ACCESS, DATABASE_1_USER_2_WRITE_OWN_ACCESS, DATABASE_1_USER_3_WRITE_ALL_ACCESS)));
         DATABASE_1_PRIVILEGED_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_1_USER_1_READ_ACCESS_DTO, DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO, DATABASE_1_USER_3_WRITE_ALL_ACCESS_DTO)));
@@ -41,7 +43,7 @@ public abstract class AbstractUnitTest extends BaseTest {
         TABLE_1.setConstraints(TABLE_1_CONSTRAINTS);
         TABLE_1_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO));
         TABLE_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO);
-        VIEW_1_DTO.setDatabase(DATABASE_1_DTO);
+        VIEW_1_DTO.setIdentifiers(VIEW_1_DTO_IDENTIFIERS);
         DATABASE_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4)));
         IDENTIFIER_1.setDatabase(DATABASE_1);
         IDENTIFIER_2.setDatabase(DATABASE_1);
@@ -49,6 +51,7 @@ public abstract class AbstractUnitTest extends BaseTest {
         IDENTIFIER_4.setDatabase(DATABASE_1);
         DATABASE_1.setTables(new LinkedList<>(List.of(TABLE_1, TABLE_2, TABLE_3, TABLE_4)));
         DATABASE_1.setViews(new LinkedList<>(List.of(VIEW_1, VIEW_2, VIEW_3)));
+        DATABASE_1_PRIVILEGED_DTO.setContainer(CONTAINER_1_PRIVILEGED_DTO);
         DATABASE_1_PRIVILEGED_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO)));
         DATABASE_1_PRIVILEGED_DTO.setTables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO)));
         DATABASE_1_PRIVILEGED_DTO.setViews(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO)));
@@ -138,6 +141,11 @@ public abstract class AbstractUnitTest extends BaseTest {
         VIEW_5_DTO.setColumns(VIEW_5_COLUMNS_DTO);
         IDENTIFIER_6.setDatabase(DATABASE_3);
         /* DATABASE 4 */
+        TABLE_9.setDatabase(DATABASE_4);
+        TABLE_9.setColumns(TABLE_9_COLUMNS);
+        TABLE_9.setConstraints(TABLE_9_CONSTRAINTS);
+        TABLE_9_DTO.setColumns(TABLE_9_COLUMNS_DTO);
+        TABLE_9_DTO.setConstraints(TABLE_9_CONSTRAINTS_DTO);
         DATABASE_4.setSubsets(new LinkedList<>());
         DATABASE_4.setAccesses(new LinkedList<>(List.of(DATABASE_4_USER_1_READ_ACCESS, DATABASE_4_USER_2_WRITE_OWN_ACCESS, DATABASE_4_USER_3_WRITE_ALL_ACCESS)));
         DATABASE_4.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_7)));
diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java
index 5e5b653852782dd6fab81dd39d1d7e5762396bfa..368b1d182c3b4d4289426673804bab23ccd8c0e1 100644
--- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java
+++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java
@@ -10,10 +10,7 @@ import at.tuwien.api.auth.RefreshTokenRequestDto;
 import at.tuwien.api.auth.SignupRequestDto;
 import at.tuwien.api.container.ContainerBriefDto;
 import at.tuwien.api.container.ContainerDto;
-import at.tuwien.api.container.image.ImageBriefDto;
-import at.tuwien.api.container.image.ImageChangeDto;
-import at.tuwien.api.container.image.ImageCreateDto;
-import at.tuwien.api.container.image.ImageDto;
+import at.tuwien.api.container.image.*;
 import at.tuwien.api.container.internal.PrivilegedContainerDto;
 import at.tuwien.api.database.*;
 import at.tuwien.api.database.internal.CreateDatabaseDto;
@@ -26,10 +23,7 @@ import at.tuwien.api.database.table.TableCreateDto;
 import at.tuwien.api.database.table.TableDto;
 import at.tuwien.api.database.table.TableStatisticDto;
 import at.tuwien.api.database.table.columns.*;
-import at.tuwien.api.database.table.columns.concepts.ConceptDto;
-import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto;
-import at.tuwien.api.database.table.columns.concepts.UnitDto;
-import at.tuwien.api.database.table.columns.concepts.UnitSaveDto;
+import at.tuwien.api.database.table.columns.concepts.*;
 import at.tuwien.api.database.table.constraints.ConstraintsCreateDto;
 import at.tuwien.api.database.table.constraints.ConstraintsDto;
 import at.tuwien.api.database.table.constraints.foreign.*;
@@ -40,11 +34,9 @@ import at.tuwien.api.datacite.DataCiteBody;
 import at.tuwien.api.datacite.DataCiteData;
 import at.tuwien.api.datacite.doi.DataCiteDoi;
 import at.tuwien.api.identifier.*;
-import at.tuwien.api.keycloak.CredentialDto;
-import at.tuwien.api.keycloak.CredentialTypeDto;
-import at.tuwien.api.keycloak.TokenDto;
-import at.tuwien.api.keycloak.UserCreateDto;
+import at.tuwien.api.keycloak.*;
 import at.tuwien.api.maintenance.BannerMessageCreateDto;
+import at.tuwien.api.maintenance.BannerMessageDto;
 import at.tuwien.api.maintenance.BannerMessageTypeDto;
 import at.tuwien.api.maintenance.BannerMessageUpdateDto;
 import at.tuwien.api.orcid.OrcidDto;
@@ -57,9 +49,9 @@ import at.tuwien.api.orcid.activities.employments.affiliation.group.summary.orga
 import at.tuwien.api.orcid.person.OrcidPersonDto;
 import at.tuwien.api.orcid.person.name.OrcidNameDto;
 import at.tuwien.api.orcid.person.name.OrcidValueDto;
-import at.tuwien.api.semantics.EntityDto;
-import at.tuwien.api.semantics.OntologyCreateDto;
-import at.tuwien.api.semantics.OntologyModifyDto;
+import at.tuwien.api.semantics.*;
+import at.tuwien.api.user.UserAttributesDto;
+import at.tuwien.api.user.UserDto;
 import at.tuwien.api.user.*;
 import at.tuwien.api.user.internal.PrivilegedUserDto;
 import at.tuwien.api.user.internal.UpdateUserPasswordDto;
@@ -111,8 +103,8 @@ import static java.time.temporal.ChronoUnit.MINUTES;
 /**
  * Database 1 (Private Data, Private Schema, User 1) -> Container 1
  * <ul>
- * <li>Table 1</li>
- * <li>Table 2</li>
+ * <li>Table 1 (Private Data, Private Schema)</li>
+ * <li>Table 2 (Private Data, Public Schema)</li>
  * <li>Table 3</li>
  * <li>Table 4</li>
  * <li>Query 1</li>
@@ -147,7 +139,9 @@ import static java.time.temporal.ChronoUnit.MINUTES;
  * </ul>
  * <p>
  * Database 4 (Public Data, Public Schema, User 4) -> Container 4
- * <li>Identifier 7 (Database=4)</li>
+ * <li>Table 9</li>
+ * <li>Identifier 7</li>
+ * <li>Query 7</li>
  * <ul>
  * </ul>
  * <br />
@@ -163,6 +157,8 @@ public abstract class BaseTest {
 
     public final static String MARIADB_IMAGE = "mariadb:11.3.2";
 
+    public final static String KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak:24.0";
+
     public final static String[] DEFAULT_SEMANTICS_HANDLING = new String[]{"default-semantics-handling",
             "create-semantic-unit", "execute-semantic-query", "table-semantic-analyse", "create-semantic-concept"};
 
@@ -194,8 +190,8 @@ public abstract class BaseTest {
             "modify-identifier-metadata", "update-foreign-identifier", "create-foreign-identifier"};
 
     public final static String[] DEFAULT_QUERY_HANDLING = new String[]{"default-query-handling", "view-table-data",
-            "execute-query", "view-table-history", "list-database-views", "list-queries", "view-database-view-data",
-            "export-query-data", "find-query", "create-database-view", "delete-database-view", "delete-table-data",
+            "execute-query", "view-table-history", "list-database-views", "view-database-view-data",
+            "export-query-data", "create-database-view", "delete-database-view", "delete-table-data",
             "export-table-data", "persist-query", "re-execute-query", "insert-table-data", "find-database-view"};
 
     public final static String[] ESCALATED_QUERY_HANDLING = new String[]{"escalated-query-handling"};
@@ -283,9 +279,12 @@ public abstract class BaseTest {
             .type(AccessTypeDto.WRITE_ALL)
             .build();
 
+    public final static String TOKEN_ACCESS_TOKEN = "ey.yee.skrr";
+    public final static String TOKEN_ACCESS_SCOPE = "openid";
+
     public final static TokenDto TOKEN_DTO = TokenDto.builder()
-            .accessToken("ey.yee.skrr")
-            .scope("openid")
+            .accessToken(TOKEN_ACCESS_TOKEN)
+            .scope(TOKEN_ACCESS_SCOPE)
             .build();
 
     public final static RefreshTokenRequestDto REFRESH_TOKEN_REQUEST_DTO = RefreshTokenRequestDto.builder()
@@ -311,6 +310,13 @@ public abstract class BaseTest {
             .description(CONCEPT_1_DESCRIPTION)
             .build();
 
+    public final static ConceptBriefDto CONCEPT_1_BRIEF_DTO = ConceptBriefDto.builder()
+            .id(CONCEPT_1_ID)
+            .uri(CONCEPT_1_URI)
+            .name(CONCEPT_1_NAME)
+            .description(CONCEPT_1_DESCRIPTION)
+            .build();
+
     public final static TableColumnConcept CONCEPT_1 = TableColumnConcept.builder()
             .id(CONCEPT_1_ID)
             .uri(CONCEPT_1_URI)
@@ -344,6 +350,13 @@ public abstract class BaseTest {
             .description(CONCEPT_2_DESCRIPTION)
             .build();
 
+    public final static ConceptBriefDto CONCEPT_2_BRIEF_DTO = ConceptBriefDto.builder()
+            .id(CONCEPT_2_ID)
+            .uri(CONCEPT_2_URI)
+            .name(CONCEPT_2_NAME)
+            .description(CONCEPT_2_DESCRIPTION)
+            .build();
+
     public final static TableColumnConcept CONCEPT_2 = TableColumnConcept.builder()
             .id(CONCEPT_2_ID)
             .uri(CONCEPT_2_URI)
@@ -371,6 +384,13 @@ public abstract class BaseTest {
             .description(UNIT_1_DESCRIPTION)
             .build();
 
+    public final static UnitBriefDto UNIT_1_BRIEF_DTO = UnitBriefDto.builder()
+            .id(UNIT_1_ID)
+            .uri(UNIT_1_URI)
+            .name(UNIT_1_NAME)
+            .description(UNIT_1_DESCRIPTION)
+            .build();
+
     public final static TableColumnUnit UNIT_1 = TableColumnUnit.builder()
             .id(UNIT_1_ID)
             .uri(UNIT_1_URI)
@@ -404,6 +424,13 @@ public abstract class BaseTest {
             .description(UNIT_2_DESCRIPTION)
             .build();
 
+    public final static UnitBriefDto UNIT_2_BRIEF_DTO = UnitBriefDto.builder()
+            .id(UNIT_2_ID)
+            .uri(UNIT_2_URI)
+            .name(UNIT_2_NAME)
+            .description(UNIT_2_DESCRIPTION)
+            .build();
+
     public final static TableColumnUnit UNIT_2 = TableColumnUnit.builder()
             .id(UNIT_2_ID)
             .uri(UNIT_2_URI)
@@ -421,11 +448,19 @@ public abstract class BaseTest {
     @SuppressWarnings("java:S2068")
     public final static String USER_LOCAL_ADMIN_PASSWORD = "admin";
     public final static String USER_LOCAL_ADMIN_THEME = "dark";
+    public final static Boolean USER_LOCAL_ADMIN_IS_INTERNAL = true;
+    public final static Boolean USER_LOCAL_ADMIN_ENABLED = true;
     public final static String USER_LOCAL_ADMIN_EMAIL = "admin@local";
     @SuppressWarnings("java:S2068")
     public final static String USER_LOCAL_ADMIN_MARIADB_PASSWORD = "*440BA4FD1A87A0999647DB67C0EE258198B247BA";
 
+    public final static LoginRequestDto USER_LOCAL_ADMIN_LOGIN_REQUEST_DTO = LoginRequestDto.builder()
+            .username(USER_LOCAL_ADMIN_USERNAME)
+            .password(USER_LOCAL_ADMIN_PASSWORD)
+            .build();
+
     public final static UserDetails USER_LOCAL_ADMIN_DETAILS = UserDetailsDto.builder()
+            .id(String.valueOf(USER_LOCAL_ADMIN_ID))
             .username(USER_LOCAL_ADMIN_USERNAME)
             .password(USER_LOCAL_ADMIN_PASSWORD)
             .authorities(AUTHORITY_DEFAULT_LOCAL_ADMIN_AUTHORITIES)
@@ -437,13 +472,14 @@ public abstract class BaseTest {
             .email(USER_LOCAL_ADMIN_EMAIL)
             .mariadbPassword(USER_LOCAL_ADMIN_MARIADB_PASSWORD)
             .theme(USER_LOCAL_ADMIN_THEME)
+            .isInternal(USER_LOCAL_ADMIN_IS_INTERNAL)
             .build();
 
     public final static Principal USER_LOCAL_ADMIN_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_LOCAL_ADMIN_DETAILS,
             USER_LOCAL_ADMIN_PASSWORD, USER_LOCAL_ADMIN_DETAILS.getAuthorities());
 
     public final static UUID USER_1_ID = UUID.fromString("cd5bab0d-7799-4069-85fb-c5d738572a0b");
-    public final static UUID USER_1_LDAP_ID = UUID.fromString("8e541e05-f45c-4d40-ba1b-0e62f04ba3f8");
+    public final static UUID USER_1_LDAP_ID = UUID.fromString("cd5bab0d-7799-4069-85fb-c5d738572a0b");
     public final static String USER_1_EMAIL = "john.doe@example.com";
     public final static String USER_1_USERNAME = "junit1";
     @SuppressWarnings("java:S2068")
@@ -454,18 +490,17 @@ public abstract class BaseTest {
     public final static String USER_1_DATABASE_PASSWORD = "*440BA4FD1A87A0999647DB67C0EE258198B247BA" /* junit1 */;
     public final static String USER_1_FIRSTNAME = "John";
     public final static String USER_1_LASTNAME = "Doe";
-    public final static String USER_1_QUALIFIED_NAME = "@" + USER_1_USERNAME + " — " + USER_1_FIRSTNAME + " " + USER_1_LASTNAME;
+    public final static String USER_1_QUALIFIED_NAME = USER_1_FIRSTNAME + " " + USER_1_LASTNAME + " — @" + USER_1_USERNAME;
     public final static String USER_1_NAME = "John Doe";
     public final static String USER_1_AFFILIATION = "TU Graz";
-    public final static String USER_1_ORCID = "000000034216302X";
-    public final static String USER_1_ORCID_UNCOMPRESSED = "0000-0003-4216-302X";
-    public final static String USER_1_ORCID_URL = "https://orcid.org/" + USER_1_ORCID_UNCOMPRESSED;
+    public final static String USER_1_ORCID_URL = "https://orcid.org/0000-0003-4216-302X";
     public final static String USER_1_TITLES_BEFORE = "Dr.";
     public final static String USER_1_TITLES_AFTER = "MSc BSc";
     public final static Boolean USER_1_VERIFIED = false;
     public final static Boolean USER_1_TOTP = false;
     public final static Long USER_1_NOT_BEFORE = 0L;
     public final static Boolean USER_1_ENABLED = true;
+    public final static Boolean USER_1_IS_INTERNAL = false;
     public final static String USER_1_THEME = "light";
     public final static String USER_1_LANGUAGE = "en";
     public final static Instant USER_1_CREATED = Instant.ofEpochSecond(1677399441L) /* 2023-02-26 08:17:21 (UTC) */;
@@ -479,7 +514,7 @@ public abstract class BaseTest {
 
     public final static UserAttributesDto USER_1_ATTRIBUTES_DTO = UserAttributesDto.builder()
             .theme(USER_1_THEME)
-            .orcid(USER_1_ORCID_UNCOMPRESSED)
+            .orcid(USER_1_ORCID_URL)
             .affiliation(USER_1_AFFILIATION)
             .mariadbPassword(USER_1_DATABASE_PASSWORD)
             .language(USER_1_LANGUAGE)
@@ -491,19 +526,31 @@ public abstract class BaseTest {
             .value(USER_1_PASSWORD)
             .build();
 
+    public final static CredentialDto USER_LOCAL_KEYCLOAK_CREDENTIAL_1 = CredentialDto.builder()
+            .type(CredentialTypeDto.PASSWORD)
+            .temporary(false)
+            .value(USER_LOCAL_ADMIN_PASSWORD)
+            .build();
+
     public final static UserCreateDto USER_1_KEYCLOAK_SIGNUP_REQUEST = UserCreateDto.builder()
             .username(USER_1_USERNAME)
             .email(USER_1_EMAIL)
             .enabled(USER_1_ENABLED)
-            .credentials(List.of(USER_1_KEYCLOAK_CREDENTIAL_1))
+            .credentials(new LinkedList<>(List.of(USER_1_KEYCLOAK_CREDENTIAL_1)))
+            .attributes(UserCreateAttributesDto.builder()
+                    .ldapId(String.valueOf(USER_1_ID))
+                    .build())
             .build();
 
-    public final static UserCreateDto USER_1_KEYCLOAK_SYSTEM_SIGNUP_REQUEST = UserCreateDto.builder()
-            .username(USER_1_USERNAME)
-            .email(USER_1_EMAIL)
-            .enabled(USER_1_ENABLED)
-            .credentials(List.of(USER_1_KEYCLOAK_CREDENTIAL_1))
-            .groups(List.of("system"))
+    public final static UserCreateDto USER_LOCAL_KEYCLOAK_SIGNUP_REQUEST = UserCreateDto.builder()
+            .username(USER_LOCAL_ADMIN_USERNAME)
+            .email(USER_LOCAL_ADMIN_EMAIL)
+            .enabled(USER_LOCAL_ADMIN_ENABLED)
+            .credentials(new LinkedList<>(List.of(USER_LOCAL_KEYCLOAK_CREDENTIAL_1)))
+            .groups(new LinkedList<>(List.of("system")))
+            .attributes(UserCreateAttributesDto.builder()
+                    .ldapId(String.valueOf(USER_LOCAL_ADMIN_ID))
+                    .build())
             .build();
 
     public final static PrivilegedUserDto USER_1_PRIVILEGED_DTO = PrivilegedUserDto.builder()
@@ -524,10 +571,11 @@ public abstract class BaseTest {
             .firstname(USER_1_FIRSTNAME)
             .lastname(USER_1_LASTNAME)
             .affiliation(USER_1_AFFILIATION)
-            .orcid(USER_1_ORCID)
+            .orcid(USER_1_ORCID_URL)
             .theme(USER_1_THEME)
             .mariadbPassword(USER_1_DATABASE_PASSWORD)
             .language(USER_1_LANGUAGE)
+            .isInternal(USER_1_IS_INTERNAL)
             .build();
 
     public final static UserDto USER_1_DTO = UserDto.builder()
@@ -544,7 +592,7 @@ public abstract class BaseTest {
             .firstname(USER_1_FIRSTNAME)
             .lastname(USER_1_LASTNAME)
             .affiliation(USER_1_AFFILIATION)
-            .orcid(USER_1_ORCID)
+            .orcid(USER_1_ORCID_URL)
             .theme(USER_1_THEME)
             .language(USER_1_LANGUAGE)
             .build();
@@ -573,6 +621,7 @@ public abstract class BaseTest {
             .lastname(USER_1_LASTNAME)
             .name(USER_1_NAME)
             .qualifiedName(USER_1_QUALIFIED_NAME)
+            .orcid(USER_1_ORCID_URL)
             .build();
 
     public final static UserDetails USER_1_DETAILS = UserDetailsDto.builder()
@@ -598,6 +647,7 @@ public abstract class BaseTest {
             .build();
 
     public final static UUID USER_2_ID = UUID.fromString("eeb9a51b-4cd8-4039-90bf-e24f17372f7c");
+    public final static UUID USER_2_LDAP_ID = UUID.fromString("eeb9a51b-4cd8-4039-90bf-e24f17372f7c");
     public final static String USER_2_EMAIL = "jane.doe@example.com";
     public final static String USER_2_USERNAME = "junit2";
     public final static String USER_2_FIRSTNAME = "Jane";
@@ -609,11 +659,12 @@ public abstract class BaseTest {
     public final static String USER_2_PASSWORD = "junit2";
     @SuppressWarnings("java:S2068")
     public final static String USER_2_DATABASE_PASSWORD = "*9AA70A8B0EEFAFCB5BED5BDEF6EE264D5DA915AE" /* junit2 */;
-    public final static String USER_2_QUALIFIED_NAME = "@" + USER_2_USERNAME + " — " + USER_2_FIRSTNAME + " " + USER_2_LASTNAME;
+    public final static String USER_2_QUALIFIED_NAME = USER_2_FIRSTNAME + " " + USER_2_LASTNAME + " — @" + USER_2_USERNAME;
     public final static Boolean USER_2_VERIFIED = true;
     public final static Boolean USER_2_TOTP = false;
     public final static Long USER_2_NOT_BEFORE = 0L;
     public final static Boolean USER_2_ENABLED = true;
+    public final static Boolean USER_2_IS_INTERNAL = false;
     public final static String USER_2_THEME = "light";
     public final static String USER_2_LANGUAGE = "de";
     public final static Instant USER_2_CREATED = Instant.ofEpochSecond(1677399528L) /* 2023-02-26 08:18:48 (UTC) */;
@@ -639,6 +690,7 @@ public abstract class BaseTest {
             .theme(USER_2_THEME)
             .mariadbPassword(USER_2_DATABASE_PASSWORD)
             .language(USER_2_LANGUAGE)
+            .isInternal(USER_2_IS_INTERNAL)
             .build();
 
     public final static UserDto USER_2_DTO = UserDto.builder()
@@ -657,6 +709,7 @@ public abstract class BaseTest {
             .firstname(USER_2_FIRSTNAME)
             .lastname(USER_2_LASTNAME)
             .name(USER_2_NAME)
+            .orcid(USER_2_ORCID_URL)
             .qualifiedName(USER_2_QUALIFIED_NAME)
             .build();
 
@@ -671,7 +724,7 @@ public abstract class BaseTest {
             .username(USER_2_USERNAME)
             .email(USER_2_EMAIL)
             .password(USER_2_PASSWORD)
-            .authorities(AUTHORITY_DEFAULT_DEVELOPER_AUTHORITIES)
+            .authorities(AUTHORITY_DEFAULT_RESEARCHER_AUTHORITIES)
             .build();
 
     public final static at.tuwien.api.keycloak.UserDto USER_2_KEYCLOAK_DTO = at.tuwien.api.keycloak.UserDto.builder()
@@ -703,6 +756,7 @@ public abstract class BaseTest {
             USER_2_PASSWORD, USER_2_DETAILS.getAuthorities());
 
     public final static UUID USER_3_ID = UUID.fromString("7b080e33-d8db-4276-9d53-47208e657006");
+    public final static UUID USER_3_LDAP_ID = UUID.fromString("7b080e33-d8db-4276-9d53-47208e657006");
     public final static String USER_3_USERNAME = "junit3";
     public final static String USER_3_FIRSTNAME = "System";
     public final static String USER_3_LASTNAME = "System";
@@ -715,11 +769,12 @@ public abstract class BaseTest {
     public final static String USER_3_PASSWORD = "password";
     @SuppressWarnings("java:S2068")
     public final static String USER_3_DATABASE_PASSWORD = "*D65FCA043964B63E849DD6334699ECB065905DA4" /* junit3 */;
-    public final static String USER_3_QUALIFIED_NAME = "@" + USER_3_USERNAME + " — " + USER_3_FIRSTNAME + " " + USER_3_LASTNAME;
+    public final static String USER_3_QUALIFIED_NAME = USER_3_FIRSTNAME + " " + USER_3_LASTNAME + " — @" + USER_3_USERNAME;
     public final static Boolean USER_3_VERIFIED = true;
     public final static Boolean USER_3_TOTP = false;
     public final static Long USER_3_NOT_BEFORE = 0L;
     public final static Boolean USER_3_ENABLED = true;
+    public final static Boolean USER_3_IS_INTERNAL = false;
     public final static String USER_3_THEME = "light";
     public final static Instant USER_3_CREATED = Instant.ofEpochSecond(1677399559L) /* 2023-02-26 08:19:19 (UTC) */;
     public final static UUID USER_3_REALM_ID = REALM_DBREPO_ID;
@@ -741,6 +796,7 @@ public abstract class BaseTest {
             .orcid(USER_3_ORCID_URL)
             .theme(USER_3_THEME)
             .mariadbPassword(USER_3_DATABASE_PASSWORD)
+            .isInternal(USER_3_IS_INTERNAL)
             .build();
 
     public final static UserDto USER_3_DTO = UserDto.builder()
@@ -749,6 +805,8 @@ public abstract class BaseTest {
             .firstname(USER_3_FIRSTNAME)
             .lastname(USER_3_LASTNAME)
             .name(USER_3_NAME)
+            .qualifiedName(USER_3_QUALIFIED_NAME)
+            .attributes(USER_3_ATTRIBUTES_DTO)
             .build();
 
     public final static UserBriefDto USER_3_BRIEF_DTO = UserBriefDto.builder()
@@ -757,6 +815,7 @@ public abstract class BaseTest {
             .firstname(USER_3_FIRSTNAME)
             .lastname(USER_3_LASTNAME)
             .name(USER_3_NAME)
+            .qualifiedName(USER_3_QUALIFIED_NAME)
             .build();
 
     public final static UserDetails USER_3_DETAILS = UserDetailsDto.builder()
@@ -764,7 +823,7 @@ public abstract class BaseTest {
             .username(USER_3_USERNAME)
             .email(USER_3_EMAIL)
             .password(USER_3_PASSWORD)
-            .authorities(AUTHORITY_DEFAULT_DATA_STEWARD_AUTHORITIES)
+            .authorities(AUTHORITY_DEFAULT_RESEARCHER_AUTHORITIES)
             .build();
 
     public final static at.tuwien.api.keycloak.UserDto USER_3_KEYCLOAK_DTO = at.tuwien.api.keycloak.UserDto.builder()
@@ -796,6 +855,7 @@ public abstract class BaseTest {
             .build();
 
     public final static UUID USER_4_ID = UUID.fromString("791d58c5-bfab-4520-b4fc-b44d4ab9feb0");
+    public final static UUID USER_4_LDAP_ID = UUID.fromString("791d58c5-bfab-4520-b4fc-b44d4ab9feb0");
     public final static String USER_4_USERNAME = "junit4";
     public final static String USER_4_FIRSTNAME = "JUnit";
     public final static String USER_4_LASTNAME = "4";
@@ -806,10 +866,11 @@ public abstract class BaseTest {
     public final static String USER_4_PASSWORD = "junit4";
     @SuppressWarnings("java:S2068")
     public final static String USER_4_DATABASE_PASSWORD = "*C20EF5C6875857DEFA9BE6E9B62DD76AAAE51882" /* junit4 */;
-    public final static String USER_4_QUALIFIED_NAME = "@" + USER_4_USERNAME + " — " + USER_4_FIRSTNAME + " " + USER_4_LASTNAME;
+    public final static String USER_4_QUALIFIED_NAME = USER_4_FIRSTNAME + " " + USER_4_LASTNAME + " — @" + USER_4_USERNAME;
     public final static String USER_4_EMAIL = "junit4@ossdip.at";
     public final static Boolean USER_4_VERIFIED = true;
     public final static Boolean USER_4_ENABLED = true;
+    public final static Boolean USER_4_IS_INTERNAL = false;
     public final static String USER_4_THEME = "light";
     public final static Instant USER_4_CREATED = Instant.ofEpochSecond(1677399592L) /* 2023-02-26 08:19:52 (UTC) */;
     public final static UUID USER_4_REALM_ID = REALM_DBREPO_ID;
@@ -831,6 +892,7 @@ public abstract class BaseTest {
             .orcid(USER_4_ORCID_URL)
             .theme(USER_4_THEME)
             .mariadbPassword(USER_4_DATABASE_PASSWORD)
+            .isInternal(USER_4_IS_INTERNAL)
             .build();
 
     public final static UserDto USER_4_DTO = UserDto.builder()
@@ -838,7 +900,9 @@ public abstract class BaseTest {
             .username(USER_4_USERNAME)
             .firstname(USER_4_FIRSTNAME)
             .lastname(USER_4_LASTNAME)
+            .name(USER_4_NAME)
             .attributes(USER_4_ATTRIBUTES_DTO)
+            .qualifiedName(USER_4_QUALIFIED_NAME)
             .build();
 
     public final static UserBriefDto USER_4_BRIEF_DTO = UserBriefDto.builder()
@@ -846,6 +910,8 @@ public abstract class BaseTest {
             .username(USER_4_USERNAME)
             .firstname(USER_4_FIRSTNAME)
             .lastname(USER_4_LASTNAME)
+            .name(USER_4_NAME)
+            .qualifiedName(USER_4_QUALIFIED_NAME)
             .build();
 
     public final static UserDetails USER_4_DETAILS = UserDetailsDto.builder()
@@ -871,20 +937,22 @@ public abstract class BaseTest {
             .build();
 
     public final static UUID USER_5_ID = UUID.fromString("28ff851d-d7bc-4422-959c-edd7a5b15630");
-    public final static String USER_5_USERNAME = "system";
-    public final static String USER_5_FIRSTNAME = "System";
-    public final static String USER_5_LASTNAME = "System";
-    public final static String USER_5_NAME = "System System";
+    public final static UUID USER_5_LDAP_ID = UUID.fromString("28ff851d-d7bc-4422-959c-edd7a5b15630");
+    public final static String USER_5_USERNAME = "nobody";
+    public final static String USER_5_FIRSTNAME = "No";
+    public final static String USER_5_LASTNAME = "Body";
+    public final static String USER_5_NAME = "No Body";
     public final static String USER_5_AFFILIATION = "TU Wien";
     public final static String USER_5_ORCID = null;
     @SuppressWarnings("java:S2068")
     public final static String USER_5_PASSWORD = "junit5";
     @SuppressWarnings("java:S2068")
     public final static String USER_5_DATABASE_PASSWORD = "*C20EF5C6875857DEFA9BE6E9B62DD76AAAE51882" /* junit5 */;
-    public final static String USER_5_QUALIFIED_NAME = "@" + USER_5_USERNAME + " — " + USER_5_FIRSTNAME + " " + USER_5_LASTNAME;
+    public final static String USER_5_QUALIFIED_NAME = USER_5_FIRSTNAME + " " + USER_5_LASTNAME + " — @" + USER_5_USERNAME;
     public final static String USER_5_EMAIL = "system@ossdip.at";
     public final static Boolean USER_5_VERIFIED = true;
     public final static Boolean USER_5_ENABLED = true;
+    public final static Boolean USER_5_IS_INTERNAL = false;
     public final static String USER_5_THEME = "dark";
     public final static Instant USER_5_CREATED = Instant.ofEpochSecond(1677399592L) /* 2023-02-26 08:19:52 (UTC) */;
     public final static UUID USER_5_REALM_ID = REALM_DBREPO_ID;
@@ -900,6 +968,7 @@ public abstract class BaseTest {
             .username(USER_5_USERNAME)
             .firstname(USER_5_FIRSTNAME)
             .lastname(USER_5_LASTNAME)
+            .name(USER_5_NAME)
             .qualifiedName(USER_5_QUALIFIED_NAME)
             .attributes(USER_5_ATTRIBUTES_DTO)
             .build();
@@ -928,7 +997,7 @@ public abstract class BaseTest {
             .username(USER_5_USERNAME)
             .email(USER_5_EMAIL)
             .password(USER_5_PASSWORD)
-            .authorities(AUTHORITY_DEFAULT_RESEARCHER_AUTHORITIES)
+            .authorities(AUTHORITY_DEFAULT_DEVELOPER_AUTHORITIES)
             .build();
 
     public final static Principal USER_5_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_5_DETAILS,
@@ -943,6 +1012,7 @@ public abstract class BaseTest {
             .affiliation(USER_5_AFFILIATION)
             .theme(USER_5_THEME)
             .mariadbPassword(USER_5_DATABASE_PASSWORD)
+            .isInternal(USER_5_IS_INTERNAL)
             .build();
 
     public final static UUID USER_6_ID = UUID.fromString("28ff851d-d7bc-4422-959c-edd7a5b15630");
@@ -959,6 +1029,7 @@ public abstract class BaseTest {
     public final static String USER_6_EMAIL = "system@ossdip.at";
     public final static Boolean USER_6_VERIFIED = true;
     public final static Boolean USER_6_ENABLED = true;
+    public final static Boolean USER_6_IS_INTERNAL = false;
     public final static Boolean USER_6_THEME_DARK = false;
     public final static Instant USER_6_CREATED = Instant.ofEpochSecond(1677399592L) /* 2023-02-26 08:19:52 (UTC) */;
     public final static UUID USER_6_REALM_ID = REALM_DBREPO_ID;
@@ -1019,6 +1090,7 @@ public abstract class BaseTest {
             .driverClass(IMAGE_1_DRIVER)
             .defaultPort(IMAGE_1_PORT)
             .isDefault(IMAGE_1_IS_DEFAULT)
+            .operators(new LinkedList<>()) /* IMAGE_1_OPERATORS */
             .build();
 
     public final static ImageDto IMAGE_1_DTO = ImageDto.builder()
@@ -1031,6 +1103,7 @@ public abstract class BaseTest {
             .driverClass(IMAGE_1_DRIVER)
             .defaultPort(IMAGE_1_PORT)
             .isDefault(IMAGE_1_IS_DEFAULT)
+            .operators(null)
             .build();
 
     public final static ImageBriefDto IMAGE_1_BRIEF_DTO = ImageBriefDto.builder()
@@ -1041,14 +1114,27 @@ public abstract class BaseTest {
             .jdbcMethod(IMAGE_1_JDBC)
             .build();
 
-    public final static List<Operator> IMAGE_1_OPERATORS = List.of(
+    public final static Long IMAGE_1_OPERATORS_1_ID = 1L;
+    public final static String IMAGE_1_OPERATORS_1_DISPLAY_NAME = "XOR";
+    public final static String IMAGE_1_OPERATORS_1_VALUE = "XOR";
+    public final static String IMAGE_1_OPERATORS_1_DOCUMENTATION = "https://mariadb.com/kb/en/xor/";
+
+    public final static List<Operator> IMAGE_1_OPERATORS = new LinkedList<>(List.of(
             Operator.builder()
-                    .id(1L)
+                    .id(IMAGE_1_OPERATORS_1_ID)
                     .image(IMAGE_1)
-                    .displayName("XOR")
-                    .value("XOR")
-                    .documentation("https://mariadb.com/kb/en/xor/")
-                    .build());
+                    .displayName(IMAGE_1_OPERATORS_1_DISPLAY_NAME)
+                    .value(IMAGE_1_OPERATORS_1_VALUE)
+                    .documentation(IMAGE_1_OPERATORS_1_DOCUMENTATION)
+                    .build()));
+
+    public final static List<OperatorDto> IMAGE_1_OPERATORS_DTO = new LinkedList<>(List.of(
+            OperatorDto.builder()
+                    .id(IMAGE_1_OPERATORS_1_ID)
+                    .displayName(IMAGE_1_OPERATORS_1_DISPLAY_NAME)
+                    .value(IMAGE_1_OPERATORS_1_VALUE)
+                    .documentation(IMAGE_1_OPERATORS_1_DOCUMENTATION)
+                    .build()));
 
     public final static Long CONTAINER_1_ID = 1L;
     public final static ContainerImage CONTAINER_1_IMAGE = IMAGE_1;
@@ -1136,7 +1222,7 @@ public abstract class BaseTest {
             .host(CONTAINER_2_HOST)
             .port(CONTAINER_2_PORT)
             .quota(CONTAINER_2_QUOTA)
-            .databases(List.of())
+            .databases(new LinkedList<>(List.of()))
             .privilegedUsername(CONTAINER_2_PRIVILEGED_USERNAME)
             .privilegedPassword(CONTAINER_2_PRIVILEGED_PASSWORD)
             .build();
@@ -1190,7 +1276,7 @@ public abstract class BaseTest {
             .host(CONTAINER_3_HOST)
             .port(CONTAINER_3_PORT)
             .quota(CONTAINER_3_QUOTA)
-            .databases(List.of())
+            .databases(new LinkedList<>(List.of()))
             .privilegedUsername(CONTAINER_3_PRIVILEGED_USERNAME)
             .privilegedPassword(CONTAINER_3_PRIVILEGED_PASSWORD)
             .build();
@@ -1294,10 +1380,6 @@ public abstract class BaseTest {
     public final static Instant DATABASE_3_CREATED = Instant.ofEpochSecond(1677399792L) /* 2023-02-26 08:23:12 (UTC) */;
     public final static Instant DATABASE_3_LAST_MODIFIED = Instant.ofEpochSecond(1677399792L) /* 2023-02-26 08:23:12 (UTC) */;
     public final static UUID DATABASE_3_OWNER = USER_3_ID;
-    public final static UUID DATABASE_3_CREATOR_ID = USER_3_ID;
-    public final static User DATABASE_3_CREATOR = USER_3;
-    public final static UserDto DATABASE_3_CREATOR_DTO = USER_3_DTO;
-    public final static UserDto DATABASE_3_OWNER_DTO = USER_3_DTO;
 
     public final static DatabaseDto DATABASE_3_DTO = DatabaseDto.builder()
             .id(DATABASE_3_ID)
@@ -1427,13 +1509,13 @@ public abstract class BaseTest {
                             .name("col13")
                             .type(ColumnTypeDto.ENUM)
                             .nullAllowed(true)
-                            .enums(List.of("val1", "val2"))
+                            .enums(new LinkedList<>(List.of("val1", "val2")))
                             .build(),
                     ColumnCreateDto.builder()
                             .name("col14")
                             .type(ColumnTypeDto.SET)
                             .nullAllowed(true)
-                            .sets(List.of("val1", "val2"))
+                            .sets(new LinkedList<>(List.of("val1", "val2")))
                             .build(),
                     ColumnCreateDto.builder()
                             .name("col15")
@@ -1600,12 +1682,11 @@ public abstract class BaseTest {
 
     public final static List<ColumnDto> TABLE_1_COLUMNS_DTO = List.of(ColumnDto.builder()
                     .id(1L)
-                    .table(TABLE_1_DTO)
                     .tableId(TABLE_1_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(0)
                     .name("id")
                     .internalName("id")
-                    .ordinalPosition(0)
                     .columnType(ColumnTypeDto.SERIAL)
                     .isNullAllowed(false)
                     .enums(null)
@@ -1613,12 +1694,11 @@ public abstract class BaseTest {
                     .build(),
             ColumnDto.builder()
                     .id(2L)
-                    .table(TABLE_1_DTO)
                     .tableId(TABLE_1_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(1)
                     .name("Date")
                     .internalName("date")
-                    .ordinalPosition(1)
                     .columnType(ColumnTypeDto.DATE)
                     .isNullAllowed(true)
                     .enums(null)
@@ -1626,12 +1706,11 @@ public abstract class BaseTest {
                     .build(),
             ColumnDto.builder()
                     .id(3L)
-                    .table(TABLE_1_DTO)
                     .tableId(TABLE_1_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(2)
                     .name("Location")
                     .internalName("location")
-                    .ordinalPosition(2)
                     .columnType(ColumnTypeDto.VARCHAR)
                     .size(255L)
                     .isNullAllowed(true)
@@ -1640,12 +1719,11 @@ public abstract class BaseTest {
                     .build(),
             ColumnDto.builder()
                     .id(4L)
-                    .table(TABLE_1_DTO)
                     .tableId(TABLE_1_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(3)
                     .name("MinTemp")
                     .internalName("mintemp")
-                    .ordinalPosition(3)
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -1655,17 +1733,16 @@ public abstract class BaseTest {
                     .build(),
             ColumnDto.builder()
                     .id(5L)
-                    .table(TABLE_1_DTO)
                     .tableId(TABLE_1_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(4)
                     .name("Rainfall")
                     .internalName("rainfall")
-                    .ordinalPosition(4)
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
-                    .concept(CONCEPT_1_DTO)
-                    .unit(UNIT_1_DTO)
+                    .concept(CONCEPT_1_BRIEF_DTO)
+                    .unit(UNIT_1_BRIEF_DTO)
                     .isNullAllowed(true)
                     .enums(null)
                     .sets(null)
@@ -1673,6 +1750,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_1_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_1_ID)
+            .databaseId(DATABASE_1_ID)
             .internalName(TABLE_1_INTERNAL_NAME)
             .isVersioned(TABLE_1_VERSIONED)
             .isPublic(TABLE_1_IS_PUBLIC)
@@ -1713,7 +1791,7 @@ public abstract class BaseTest {
     public final static String TABLE_2_INTERNALNAME = "weather_location";
     public final static Boolean TABLE_2_VERSIONED = true;
     public final static Boolean TABLE_2_IS_PUBLIC = false;
-    public final static Boolean TABLE_2_SCHEMA_PUBLIC = false;
+    public final static Boolean TABLE_2_SCHEMA_PUBLIC = true;
     public final static Boolean TABLE_2_PROCESSED_CONSTRAINTS = true;
     public final static String TABLE_2_DESCRIPTION = "Weather location";
     public final static String TABLE_2_QUEUE_NAME = TABLE_2_INTERNALNAME;
@@ -1793,6 +1871,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_2_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_2_ID)
+            .databaseId(DATABASE_1_ID)
             .internalName(TABLE_2_INTERNALNAME)
             .isVersioned(TABLE_2_VERSIONED)
             .isPublic(TABLE_2_IS_PUBLIC)
@@ -1864,6 +1943,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_3_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_3_ID)
+            .databaseId(DATABASE_1_ID)
             .internalName(TABLE_3_INTERNALNAME)
             .isVersioned(TABLE_3_VERSIONED)
             .isPublic(TABLE_3_IS_PUBLIC)
@@ -1886,8 +1966,9 @@ public abstract class BaseTest {
             .uniques(new LinkedList<>())
             .foreignKeys(List.of(ForeignKeyCreateDto.builder()
                     .referencedTable("weather_location")
-                    .columns(List.of("fahrzeug"))
-                    .referencedColumns(List.of("doesnotexist")).build()))
+                    .columns(new LinkedList<>(List.of("fahrzeug")))
+                    .referencedColumns(new LinkedList<>(List.of("doesnotexist")))
+                    .build()))
             .build();
 
     public final static TableCreateDto TABLE_3_CREATE_DTO = TableCreateDto.builder()
@@ -1981,6 +2062,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_5_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_5_ID)
+            .databaseId(DATABASE_2_ID)
             .internalName(TABLE_5_INTERNALNAME)
             .isVersioned(TABLE_5_VERSIONED)
             .isPublic(TABLE_5_IS_PUBLIC)
@@ -2040,6 +2122,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_6_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_6_ID)
+            .databaseId(DATABASE_2_ID)
             .internalName(TABLE_6_INTERNALNAME)
             .isVersioned(TABLE_6_VERSIONED)
             .isPublic(TABLE_6_IS_PUBLIC)
@@ -2099,6 +2182,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_7_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_7_ID)
+            .databaseId(DATABASE_2_ID)
             .internalName(TABLE_7_INTERNAL_NAME)
             .isVersioned(TABLE_7_VERSIONED)
             .isPublic(TABLE_7_IS_PUBLIC)
@@ -2170,6 +2254,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_4_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_4_ID)
+            .databaseId(DATABASE_1_ID)
             .internalName(TABLE_4_INTERNALNAME)
             .description(TABLE_4_DESCRIPTION)
             .name(TABLE_4_NAME)
@@ -2226,7 +2311,7 @@ public abstract class BaseTest {
             .checks(new LinkedHashSet<>())
             .primaryKey(new LinkedHashSet<>(Set.of("Timestamp")))
             .foreignKeys(new LinkedList<>())
-            .uniques(List.of(List.of("Timestamp")))
+            .uniques(new LinkedList<>(List.of(List.of("Timestamp"))))
             .build();
 
     public final static TableCreateDto TABLE_4_CREATE_DTO = TableCreateDto.builder()
@@ -2264,7 +2349,7 @@ public abstract class BaseTest {
 
     public final static Long TABLE_8_ID = 8L;
     public final static Long TABLE_8_DATABASE_ID = DATABASE_3_ID;
-    public final static String TABLE_8_NAME = "mfcc";
+    public final static String TABLE_8_NAME = "location";
     public final static String TABLE_8_INTERNAL_NAME = "mfcc";
     public final static Boolean TABLE_8_VERSIONED = true;
     public final static Boolean TABLE_8_IS_PUBLIC = false;
@@ -2312,6 +2397,7 @@ public abstract class BaseTest {
 
     public final static TableBriefDto TABLE_8_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_8_ID)
+            .databaseId(DATABASE_3_ID)
             .internalName(TABLE_8_INTERNAL_NAME)
             .description(TABLE_8_DESCRIPTION)
             .isVersioned(TABLE_8_VERSIONED)
@@ -2337,6 +2423,194 @@ public abstract class BaseTest {
             .lastRetrieved(Instant.now())
             .build();
 
+    public final static Long TABLE_9_ID = 9L;
+    public final static Long TABLE_9_DATABASE_ID = DATABASE_4_ID;
+    public final static String TABLE_9_NAME = "mfcc";
+    public final static String TABLE_9_INTERNAL_NAME = "mfcc";
+    public final static Boolean TABLE_9_VERSIONED = true;
+    public final static Boolean TABLE_9_IS_PUBLIC = false;
+    public final static Boolean TABLE_9_SCHEMA_PUBLIC = true;
+    public final static Boolean TABLE_9_PROCESSED_CONSTRAINTS = true;
+    public final static String TABLE_9_DESCRIPTION = "Hello mfcc";
+    public final static String TABLE_9_QUEUE_NAME = TABLE_9_INTERNAL_NAME;
+    public final static String TABLE_9_ROUTING_KEY = "dbrepo\\." + DATABASE_3_ID + "\\." + TABLE_9_ID;
+    public final static Instant TABLE_9_CREATED = Instant.ofEpochSecond(1688400185L) /* 2023-02-26 08:29:35 (UTC) */;
+    public final static Instant TABLE_9_LAST_MODIFIED = Instant.ofEpochSecond(1688400185L) /* 2023-02-26 08:29:35 (UTC) */;
+
+    public final static Table TABLE_9 = Table.builder()
+            .id(TABLE_9_ID)
+            .tdbid(TABLE_9_DATABASE_ID)
+            .internalName(TABLE_9_INTERNAL_NAME)
+            .description(TABLE_9_DESCRIPTION)
+            .isVersioned(TABLE_9_VERSIONED)
+            .isPublic(TABLE_9_IS_PUBLIC)
+            .isSchemaPublic(TABLE_9_SCHEMA_PUBLIC)
+            .database(null /* DATABASE_1 */)
+            .name(TABLE_9_NAME)
+            .queueName(TABLE_9_QUEUE_NAME)
+            .columns(new LinkedList<>()) /* TABLE_9_COLUMNS */
+            .constraints(null) /* TABLE_9_CONSTRAINTS */
+            .ownedBy(USER_1_ID)
+            .owner(USER_1)
+            .created(TABLE_9_CREATED)
+            .lastModified(TABLE_9_LAST_MODIFIED)
+            .build();
+
+    public final static TableDto TABLE_9_DTO = TableDto.builder()
+            .id(TABLE_9_ID)
+            .tdbid(TABLE_9_DATABASE_ID)
+            .internalName(TABLE_9_INTERNAL_NAME)
+            .description(TABLE_9_DESCRIPTION)
+            .isVersioned(TABLE_9_VERSIONED)
+            .isPublic(TABLE_9_IS_PUBLIC)
+            .isSchemaPublic(TABLE_9_SCHEMA_PUBLIC)
+            .name(TABLE_9_NAME)
+            .queueName(TABLE_9_QUEUE_NAME)
+            .columns(new LinkedList<>()) /* TABLE_9_COLUMNS_DTO */
+            .constraints(null) /* TABLE_9_CONSTRAINTS_DTO */
+            .owner(USER_1_BRIEF_DTO)
+            .build();
+
+    public final static TableBriefDto TABLE_9_BRIEF_DTO = TableBriefDto.builder()
+            .id(TABLE_9_ID)
+            .databaseId(DATABASE_4_ID)
+            .internalName(TABLE_9_INTERNAL_NAME)
+            .description(TABLE_9_DESCRIPTION)
+            .isVersioned(TABLE_9_VERSIONED)
+            .isPublic(TABLE_9_IS_PUBLIC)
+            .isSchemaPublic(TABLE_9_SCHEMA_PUBLIC)
+            .name(TABLE_9_NAME)
+            .ownedBy(USER_1_ID)
+            .build();
+
+    public final static PrivilegedTableDto TABLE_9_PRIVILEGED_DTO = PrivilegedTableDto.builder()
+            .id(TABLE_9_ID)
+            .tdbid(TABLE_9_DATABASE_ID)
+            .internalName(TABLE_9_INTERNAL_NAME)
+            .description(TABLE_9_DESCRIPTION)
+            .isVersioned(TABLE_9_VERSIONED)
+            .isPublic(TABLE_9_IS_PUBLIC)
+            .isSchemaPublic(TABLE_9_SCHEMA_PUBLIC)
+            .name(TABLE_9_NAME)
+            .queueName(TABLE_9_QUEUE_NAME)
+            .columns(new LinkedList<>()) /* TABLE_9_COLUMNS_DTO */
+            .owner(USER_1_BRIEF_DTO)
+            .isPublic(DATABASE_3_PUBLIC)
+            .lastRetrieved(Instant.now())
+            .build();
+
+    public final static Long COLUMN_9_1_ID = 78L;
+    public final static String COLUMN_9_1_NAME = "location";
+    public final static String COLUMN_9_1_INTERNAL_NAME = "location";
+
+    public final static ColumnBriefDto TABLE_9_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder()
+            .id(COLUMN_9_1_ID)
+            .name(COLUMN_9_1_NAME)
+            .internalName(COLUMN_9_1_INTERNAL_NAME)
+            .columnType(ColumnTypeDto.BIGINT)
+            .build();
+
+    public final static Long COLUMN_9_2_ID = 79L;
+
+    public final static Long COLUMN_9_3_ID = 80L;
+
+    public final static List<TableColumn> TABLE_9_COLUMNS = List.of(TableColumn.builder()
+                    .id(COLUMN_9_1_ID)
+                    .ordinalPosition(0)
+                    .table(TABLE_9)
+                    .name(COLUMN_9_1_NAME)
+                    .internalName(COLUMN_9_1_INTERNAL_NAME)
+                    .columnType(TableColumnType.VARCHAR)
+                    .size(255L)
+                    .isNullAllowed(false)
+                    .enums(null)
+                    .sets(null)
+                    .build(),
+            TableColumn.builder()
+                    .id(COLUMN_9_2_ID)
+                    .ordinalPosition(1)
+                    .table(TABLE_9)
+                    .name("lat")
+                    .internalName("lat")
+                    .columnType(TableColumnType.DECIMAL)
+                    .size(10L)
+                    .d(0L)
+                    .isNullAllowed(true)
+                    .enums(null)
+                    .sets(null)
+                    .build(),
+            TableColumn.builder()
+                    .id(COLUMN_9_3_ID)
+                    .ordinalPosition(2)
+                    .table(TABLE_9)
+                    .name("lng")
+                    .internalName("lng")
+                    .columnType(TableColumnType.DECIMAL)
+                    .size(10L)
+                    .d(0L)
+                    .isNullAllowed(true)
+                    .enums(null)
+                    .sets(null)
+                    .build());
+
+    public final static List<ColumnDto> TABLE_9_COLUMNS_DTO = List.of(ColumnDto.builder()
+                    .id(COLUMN_9_1_ID)
+                    .ordinalPosition(0)
+                    .name(COLUMN_9_1_NAME)
+                    .internalName(COLUMN_9_1_INTERNAL_NAME)
+                    .columnType(ColumnTypeDto.VARCHAR)
+                    .size(255L)
+                    .isNullAllowed(false)
+                    .enums(null)
+                    .sets(null)
+                    .build(),
+            ColumnDto.builder()
+                    .id(COLUMN_9_2_ID)
+                    .ordinalPosition(1)
+                    .name("lat")
+                    .internalName("lat")
+                    .columnType(ColumnTypeDto.DECIMAL)
+                    .size(10L)
+                    .d(0L)
+                    .isNullAllowed(true)
+                    .enums(null)
+                    .sets(null)
+                    .build(),
+            ColumnDto.builder()
+                    .id(COLUMN_9_3_ID)
+                    .ordinalPosition(2)
+                    .name("lng")
+                    .internalName("lng")
+                    .columnType(ColumnTypeDto.DECIMAL)
+                    .size(10L)
+                    .d(0L)
+                    .isNullAllowed(true)
+                    .enums(null)
+                    .sets(null)
+                    .build());
+
+    public final static Constraints TABLE_9_CONSTRAINTS = Constraints.builder()
+            .checks(new LinkedHashSet<>())
+            .foreignKeys(new LinkedList<>())
+            .uniques(new LinkedList<>())
+            .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder()
+                    .table(TABLE_9)
+                    .column(TABLE_9_COLUMNS.get(0))
+                    .id(9L)
+                    .build())))
+            .build();
+
+    public final static ConstraintsDto TABLE_9_CONSTRAINTS_DTO = ConstraintsDto.builder()
+            .checks(new LinkedHashSet<>())
+            .foreignKeys(new LinkedList<>())
+            .uniques(new LinkedList<>())
+            .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder()
+                    .table(TABLE_9_BRIEF_DTO)
+                    .column(TABLE_9_COLUMNS_BRIEF_0_DTO)
+                    .id(9L)
+                    .build())))
+            .build();
+
     public final static String QUEUE_NAME = "dbrepo";
     public final static String QUEUE_VHOST = "dbrepo";
     public final static Boolean QUEUE_AUTO_DELETE = false;
@@ -2359,7 +2633,9 @@ public abstract class BaseTest {
     public final static String ONTOLOGY_1_URI = "http://www.ontology-of-units-of-measure.org/resource/om-2/";
     public final static String ONTOLOGY_1_URI_PATTERN = "http://www.ontology-of-units-of-measure.org/resource/om-2/.*";
     public final static String ONTOLOGY_1_SPARQL_ENDPOINT = null;
+    public final static Boolean ONTOLOGY_1_SPARQL = false;
     public final static String ONTOLOGY_1_RDF_PATH = "rdf/om-2.0.rdf";
+    public final static Boolean ONTOLOGY_1_RDF = true;
     public final static UUID ONTOLOGY_1_CREATED_BY = USER_1_ID;
 
     public final static Ontology ONTOLOGY_1 = Ontology.builder()
@@ -2371,6 +2647,26 @@ public abstract class BaseTest {
             .rdfPath(ONTOLOGY_1_RDF_PATH)
             .build();
 
+    public final static OntologyDto ONTOLOGY_1_DTO = OntologyDto.builder()
+            .id(ONTOLOGY_1_ID)
+            .prefix(ONTOLOGY_1_PREFIX)
+            .uri(ONTOLOGY_1_URI)
+            .uriPattern(ONTOLOGY_1_URI_PATTERN)
+            .sparqlEndpoint(ONTOLOGY_1_SPARQL_ENDPOINT)
+            .sparql(ONTOLOGY_1_SPARQL)
+            .rdfPath(ONTOLOGY_1_RDF_PATH)
+            .rdf(ONTOLOGY_1_RDF)
+            .build();
+
+    public final static OntologyBriefDto ONTOLOGY_1_BRIEF_DTO = OntologyBriefDto.builder()
+            .id(ONTOLOGY_1_ID)
+            .prefix(ONTOLOGY_1_PREFIX)
+            .uri(ONTOLOGY_1_URI)
+            .uriPattern(ONTOLOGY_1_URI_PATTERN)
+            .sparql(ONTOLOGY_1_SPARQL)
+            .rdf(ONTOLOGY_1_RDF)
+            .build();
+
     public final static OntologyCreateDto ONTOLOGY_1_CREATE_DTO = OntologyCreateDto.builder()
             .prefix(ONTOLOGY_1_PREFIX)
             .uri(ONTOLOGY_1_URI)
@@ -2528,7 +2824,6 @@ public abstract class BaseTest {
     public final static List<ColumnDto> TABLE_8_COLUMNS_DTO = List.of(ColumnDto.builder()
                     .id(COLUMN_8_1_ID)
                     .ordinalPosition(COLUMN_8_1_ORDINALPOS)
-                    .table(TABLE_8_DTO)
                     .name(COLUMN_8_1_NAME)
                     .internalName(COLUMN_8_1_INTERNAL_NAME)
                     .columnType(COLUMN_8_1_TYPE_DTO)
@@ -2537,7 +2832,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_8_2_ID)
                     .ordinalPosition(COLUMN_8_2_ORDINALPOS)
-                    .table(TABLE_8_DTO)
                     .name(COLUMN_8_2_NAME)
                     .internalName(COLUMN_8_2_INTERNAL_NAME)
                     .columnType(COLUMN_8_2_TYPE_DTO)
@@ -2546,7 +2840,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_8_3_ID)
                     .ordinalPosition(COLUMN_8_3_ORDINALPOS)
-                    .table(TABLE_8_DTO)
                     .name(COLUMN_8_3_NAME)
                     .internalName(COLUMN_8_3_INTERNAL_NAME)
                     .columnType(COLUMN_8_3_TYPE_DTO)
@@ -2587,7 +2880,7 @@ public abstract class BaseTest {
                 put(COLUMN_8_3_INTERNAL_NAME, null);
             }}
     ));
-    
+
     @SuppressWarnings("java:S3599")
     public final static TableStatisticDto TABLE_8_STATISTIC_DTO = TableStatisticDto.builder()
             .columns(new HashMap<>() {{
@@ -2686,8 +2979,31 @@ public abstract class BaseTest {
             .isPersisted(QUERY_3_PERSISTED)
             .resultNumber(2L)
             .build();
-
+    
+    public final static Long QUERY_7_ID = 7L;
     public final static String QUERY_7_STATEMENT = "SELECT id, date, a.location, lat, lng FROM weather_aus a JOIN weather_location l on a.location = l.location WHERE date = '2008-12-01'";
+    public final static String QUERY_7_QUERY_HASH = "df7da3801dfb5c191ff6711d79ce6455f3c09ec8323ce1ff7208ab85387263f5";
+    public final static String QUERY_7_RESULT_HASH = "ff4f7cbe1b96d496957f6e49e55b8b1b577fa4d405d4795af99594cfd40cb80d";
+    public final static Instant QUERY_7_CREATED = Instant.now().minus(4, MINUTES);
+    public final static Instant QUERY_7_EXECUTION = Instant.now().minus(1, MINUTES);
+    public final static Instant QUERY_7_LAST_MODIFIED = Instant.ofEpochSecond(1541588454L);
+    public final static Long QUERY_7_RESULT_NUMBER = 6L;
+    public final static Long QUERY_7_RESULT_ID = 4L;
+    public final static Boolean QUERY_7_PERSISTED = false;
+
+    public final static QueryDto QUERY_7_DTO = QueryDto.builder()
+            .id(QUERY_7_ID)
+            .databaseId(DATABASE_4_ID)
+            .query(QUERY_7_STATEMENT)
+            .queryNormalized(QUERY_7_STATEMENT)
+            .resultNumber(QUERY_7_RESULT_NUMBER)
+            .resultHash(QUERY_7_RESULT_HASH)
+            .owner(USER_1_BRIEF_DTO)
+            .queryHash(QUERY_7_QUERY_HASH)
+            .execution(QUERY_7_EXECUTION)
+            .isPersisted(QUERY_7_PERSISTED)
+            .resultNumber(2L)
+            .build();
 
     public final static Long QUERY_4_ID = 4L;
     public final static String QUERY_4_STATEMENT = "SELECT `id`, `value` FROM `mfcc`";
@@ -2914,14 +3230,14 @@ public abstract class BaseTest {
             .checks(new LinkedHashSet<>())
             .primaryKey(new LinkedHashSet<>(List.of("id")))
             .foreignKeys(new LinkedList<>())
-            .uniques(List.of(List.of("date")))
+            .uniques(new LinkedList<>(List.of(List.of("date"))))
             .build();
 
     public final static ConstraintsCreateDto TABLE_1_CONSTRAINTS_CREATE_INVALID_DTO = ConstraintsCreateDto.builder()
             .checks(new LinkedHashSet<>())
             .primaryKey(new LinkedHashSet<>())
             .foreignKeys(new LinkedList<>())
-            .uniques(List.of(List.of("date")))
+            .uniques(new LinkedList<>(List.of(List.of("date"))))
             .build();
 
     public final static TableCreateDto TABLE_1_CREATE_DTO = TableCreateDto.builder()
@@ -2957,7 +3273,6 @@ public abstract class BaseTest {
                     .table(TABLE_2)
                     .name("location")
                     .internalName("location")
-                    .ordinalPosition(0)
                     .columnType(TableColumnType.VARCHAR)
                     .size(255L)
                     .isNullAllowed(false)
@@ -2970,7 +3285,6 @@ public abstract class BaseTest {
                     .table(TABLE_2)
                     .name("lat")
                     .internalName("lat")
-                    .ordinalPosition(1)
                     .columnType(TableColumnType.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -2984,7 +3298,6 @@ public abstract class BaseTest {
                     .table(TABLE_2)
                     .name("lng")
                     .internalName("lng")
-                    .ordinalPosition(2)
                     .columnType(TableColumnType.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -3009,12 +3322,11 @@ public abstract class BaseTest {
 
     public final static List<ColumnDto> TABLE_2_COLUMNS_DTO = List.of(ColumnDto.builder()
                     .id(COLUMN_2_1_ID)
-                    .table(TABLE_2_DTO)
                     .tableId(TABLE_2_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(0)
                     .name("location")
                     .internalName("location")
-                    .ordinalPosition(0)
                     .columnType(ColumnTypeDto.VARCHAR)
                     .size(255L)
                     .isNullAllowed(false)
@@ -3023,12 +3335,11 @@ public abstract class BaseTest {
                     .build(),
             ColumnDto.builder()
                     .id(COLUMN_2_2_ID)
-                    .table(TABLE_2_DTO)
                     .tableId(TABLE_2_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(1)
                     .name("lat")
                     .internalName("lat")
-                    .ordinalPosition(1)
                     .columnType(ColumnTypeDto.DOUBLE)
                     .size(22L)
                     .isNullAllowed(true)
@@ -3037,12 +3348,11 @@ public abstract class BaseTest {
                     .build(),
             ColumnDto.builder()
                     .id(COLUMN_2_3_ID)
-                    .table(TABLE_2_DTO)
                     .tableId(TABLE_2_ID)
                     .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(2)
                     .name("lng")
                     .internalName("lng")
-                    .ordinalPosition(2)
                     .columnType(ColumnTypeDto.DOUBLE)
                     .size(22L)
                     .isNullAllowed(true)
@@ -3516,7 +3826,6 @@ public abstract class BaseTest {
     public final static List<ColumnDto> TABLE_3_COLUMNS_DTO = List.of(ColumnDto.builder()
                     .id(COLUMN_3_1_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.BIGINT)
                     .name("id")
@@ -3528,7 +3837,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_2_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("linie")
@@ -3540,7 +3848,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_3_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("richtung")
@@ -3552,7 +3859,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_4_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.DATE)
                     .name("betriebsdatum")
@@ -3564,7 +3870,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_5_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("fahrzeug")
@@ -3576,7 +3881,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_6_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("kurs")
@@ -3588,7 +3892,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_7_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("seq_von")
@@ -3600,7 +3903,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_8_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_diva_von")
@@ -3612,7 +3914,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_9_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_punkt_diva_von")
@@ -3624,7 +3925,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_10_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_kurz_von1")
@@ -3636,7 +3936,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_11_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.DATE)
                     .name("datum_von")
@@ -3648,7 +3947,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_12_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("soll_an_von")
@@ -3660,7 +3958,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_13_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("ist_an_von")
@@ -3672,7 +3969,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_14_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("soll_ab_von")
@@ -3684,7 +3980,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_15_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("ist_ab_von")
@@ -3696,7 +3991,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_16_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("seq_nach")
@@ -3708,7 +4002,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_17_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_diva_nach")
@@ -3720,7 +4013,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_18_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_punkt_diva_nach")
@@ -3732,7 +4024,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_19_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_kurz_nach1")
@@ -3744,7 +4035,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_20_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.DATE)
                     .name("datum_nach")
@@ -3756,7 +4046,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_21_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("soll_an_nach")
@@ -3768,7 +4057,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_22_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("ist_an_nach1")
@@ -3780,7 +4068,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_23_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("soll_ab_nach")
@@ -3792,7 +4079,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_24_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("ist_ab_nach")
@@ -3804,7 +4090,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_25_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("fahrt_id")
@@ -3816,7 +4101,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_26_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("fahrweg_id")
@@ -3828,7 +4112,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_27_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("fw_no")
@@ -3840,7 +4123,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_28_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("fw_typ")
@@ -3852,7 +4134,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_29_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("fw_kurz")
@@ -3864,7 +4145,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_30_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("fw_lang")
@@ -3876,7 +4156,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_31_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("umlauf_von")
@@ -3888,7 +4167,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_32_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_id_von")
@@ -3900,7 +4178,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_33_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_id_nach")
@@ -3912,7 +4189,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_34_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_punkt_id_von")
@@ -3924,7 +4200,6 @@ public abstract class BaseTest {
             ColumnDto.builder()
                     .id(COLUMN_3_35_ID)
                     .tableId(TABLE_3_ID)
-                    .table(TABLE_3_DTO)
                     .databaseId(DATABASE_1_ID)
                     .columnType(ColumnTypeDto.INT)
                     .name("halt_punkt_id_nach")
@@ -4177,7 +4452,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_1_ID)
                     .ordinalPosition(0)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("id")
                     .internalName("id")
                     .columnType(ColumnTypeDto.BIGINT)
@@ -4187,7 +4461,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_2_ID)
                     .ordinalPosition(1)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Animal Name")
                     .internalName("animal_name")
                     .columnType(ColumnTypeDto.VARCHAR)
@@ -4197,7 +4470,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_3_ID)
                     .ordinalPosition(2)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Hair")
                     .internalName("hair")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4207,7 +4479,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_4_ID)
                     .ordinalPosition(3)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Feathers")
                     .internalName("feathers")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4217,7 +4488,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_5_ID)
                     .ordinalPosition(4)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Bread")
                     .internalName("bread")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4227,7 +4497,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_6_ID)
                     .ordinalPosition(5)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Eggs")
                     .internalName("eggs")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4237,7 +4506,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_7_ID)
                     .ordinalPosition(6)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Milk")
                     .internalName("milk")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4247,7 +4515,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_8_ID)
                     .ordinalPosition(7)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Water")
                     .internalName("water")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4257,7 +4524,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_9_ID)
                     .ordinalPosition(8)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Airborne")
                     .internalName("airborne")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4267,7 +4533,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_10_ID)
                     .ordinalPosition(9)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Waterborne")
                     .internalName("waterborne")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4277,7 +4542,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_11_ID)
                     .ordinalPosition(10)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Aquantic")
                     .internalName("aquantic")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4287,7 +4551,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_12_ID)
                     .ordinalPosition(11)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Predator")
                     .internalName("predator")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4297,7 +4560,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_13_ID)
                     .ordinalPosition(12)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Backbone")
                     .internalName("backbone")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4307,7 +4569,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_14_ID)
                     .ordinalPosition(13)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Breathes")
                     .internalName("breathes")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4317,7 +4578,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_15_ID)
                     .ordinalPosition(14)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Venomous")
                     .internalName("venomous")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4327,7 +4587,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_16_ID)
                     .ordinalPosition(15)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Fin")
                     .internalName("fin")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4337,7 +4596,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_17_ID)
                     .ordinalPosition(16)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Legs")
                     .internalName("legs")
                     .columnType(ColumnTypeDto.INT)
@@ -4347,7 +4605,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_18_ID)
                     .ordinalPosition(17)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Tail")
                     .internalName("tail")
                     .columnType(ColumnTypeDto.DECIMAL)
@@ -4357,7 +4614,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_19_ID)
                     .ordinalPosition(18)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Domestic")
                     .internalName("domestic")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4367,7 +4623,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_20_ID)
                     .ordinalPosition(19)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Catsize")
                     .internalName("catsize")
                     .columnType(ColumnTypeDto.BOOL)
@@ -4377,7 +4632,6 @@ public abstract class BaseTest {
                     .id(COLUMN_5_21_ID)
                     .ordinalPosition(20)
                     .tableId(TABLE_5_ID)
-                    .table(TABLE_5_DTO)
                     .name("Class Type")
                     .internalName("class_type")
                     .columnType(ColumnTypeDto.DECIMAL)
@@ -4385,9 +4639,9 @@ public abstract class BaseTest {
                     .build());
 
     public final static List<ForeignKeyCreateDto> TABLE_5_FOREIGN_KEYS_INVALID_CREATE = List.of(ForeignKeyCreateDto.builder()
-            .columns(List.of("somecolumn"))
+            .columns(new LinkedList<>(List.of("somecolumn")))
             .referencedTable("sometable")
-            .referencedColumns(List.of("someothercolumn"))
+            .referencedColumns(new LinkedList<>(List.of("someothercolumn")))
             .build());
 
     public final static ConstraintsCreateDto TABLE_5_CONSTRAINTS_INVALID_CREATE = ConstraintsCreateDto.builder()
@@ -4502,7 +4756,7 @@ public abstract class BaseTest {
 
     public final static ConstraintsCreateDto TABLE_5_CREATE_CONSTRAINTS_DTO = ConstraintsCreateDto.builder()
             .primaryKey(Set.of("id"))
-            .uniques(List.of(List.of("id")))
+            .uniques(new LinkedList<>(List.of(List.of("id"))))
             .checks(new LinkedHashSet<>())
             .foreignKeys(new LinkedList<>())
             .build();
@@ -4587,7 +4841,6 @@ public abstract class BaseTest {
                     .id(67L)
                     .ordinalPosition(0)
                     .tableId(TABLE_6_ID)
-                    .table(TABLE_6_DTO)
                     .name("id")
                     .internalName("id")
                     .columnType(ColumnTypeDto.BIGINT)
@@ -4597,7 +4850,6 @@ public abstract class BaseTest {
                     .id(68L)
                     .ordinalPosition(1)
                     .tableId(TABLE_6_ID)
-                    .table(TABLE_6_DTO)
                     .name("firstname")
                     .internalName("firstname")
                     .columnType(ColumnTypeDto.VARCHAR)
@@ -4607,7 +4859,6 @@ public abstract class BaseTest {
                     .id(69L)
                     .ordinalPosition(2)
                     .tableId(TABLE_6_ID)
-                    .table(TABLE_6_DTO)
                     .name("lastname")
                     .internalName("lastname")
                     .columnType(ColumnTypeDto.VARCHAR)
@@ -4617,7 +4868,6 @@ public abstract class BaseTest {
                     .id(70L)
                     .ordinalPosition(3)
                     .tableId(TABLE_6_ID)
-                    .table(TABLE_6_DTO)
                     .name("birth")
                     .internalName("birth")
                     .columnType(ColumnTypeDto.YEAR)
@@ -4627,7 +4877,6 @@ public abstract class BaseTest {
                     .id(71L)
                     .ordinalPosition(4)
                     .tableId(TABLE_6_ID)
-                    .table(TABLE_6_DTO)
                     .name("reminder")
                     .internalName("reminder")
                     .columnType(ColumnTypeDto.TIME)
@@ -4637,7 +4886,6 @@ public abstract class BaseTest {
                     .id(72L)
                     .ordinalPosition(5)
                     .tableId(TABLE_6_ID)
-                    .table(TABLE_6_DTO)
                     .name("ref_id")
                     .internalName("ref_id")
                     .columnType(ColumnTypeDto.BIGINT)
@@ -4648,9 +4896,9 @@ public abstract class BaseTest {
             List.of("firstname", "lastname"));
 
     public final static List<ForeignKeyCreateDto> TABLE_6_FOREIGN_KEYS_CREATE = List.of(ForeignKeyCreateDto.builder()
-            .columns(List.of("ref_id"))
+            .columns(new LinkedList<>(List.of("ref_id")))
             .referencedTable("zoo")
-            .referencedColumns(List.of("id"))
+            .referencedColumns(new LinkedList<>(List.of("id")))
             .build());
 
     public final static Set<String> TABLE_6_CHECKS_CREATE = Set.of("firstname != lastname");
@@ -4723,7 +4971,6 @@ public abstract class BaseTest {
                     .id(COLUMN_7_1_ID)
                     .ordinalPosition(0)
                     .tableId(TABLE_7_ID)
-                    .table(TABLE_7_DTO)
                     .name("name_id")
                     .internalName("name_id")
                     .columnType(ColumnTypeDto.BIGINT)
@@ -4733,7 +4980,6 @@ public abstract class BaseTest {
                     .id(COLUMN_7_2_ID)
                     .ordinalPosition(1)
                     .tableId(TABLE_7_ID)
-                    .table(TABLE_7_DTO)
                     .name("zoo_id")
                     .internalName("zoo_id")
                     .columnType(ColumnTypeDto.BIGINT)
@@ -4754,34 +5000,37 @@ public abstract class BaseTest {
     public final static List<ViewColumnDto> VIEW_1_COLUMNS_DTO = List.of(
             ViewColumnDto.builder()
                     .id(1L)
+                    .ordinalPosition(0)
                     .databaseId(DATABASE_1_ID)
                     .name("location")
                     .internalName("location")
-                    .ordinalPosition(0)
                     .columnType(ColumnTypeDto.VARCHAR)
                     .size(255L)
                     .isNullAllowed(false)
                     .build(),
             ViewColumnDto.builder()
                     .id(2L)
+                    .ordinalPosition(1)
                     .databaseId(DATABASE_1_ID)
                     .name("lat")
                     .internalName("lat")
-                    .ordinalPosition(1)
-                    .columnType(ColumnTypeDto.DOUBLE)
-                    .size(22L)
+                    .columnType(ColumnTypeDto.DECIMAL)
+                    .size(10L)
+                    .d(0L)
                     .isNullAllowed(true)
                     .build(),
             ViewColumnDto.builder()
                     .id(3L)
+                    .ordinalPosition(2)
                     .databaseId(DATABASE_1_ID)
                     .name("lng")
                     .internalName("lng")
-                    .ordinalPosition(2)
-                    .columnType(ColumnTypeDto.DOUBLE)
-                    .size(22L)
+                    .columnType(ColumnTypeDto.DECIMAL)
+                    .size(10L)
+                    .d(0L)
                     .isNullAllowed(true)
-                    .build());
+                    .build()
+    );
 
     public final static View VIEW_1 = View.builder()
             .id(VIEW_1_ID)
@@ -4795,6 +5044,7 @@ public abstract class BaseTest {
             .queryHash(VIEW_1_QUERY_HASH)
             .ownedBy(USER_1_ID)
             .owner(USER_1)
+            .identifiers(new LinkedList<>()) /* IDENTIFIER_3 */
             .columns(null) /* VIEW_1_COLUMNS */
             .build();
 
@@ -4823,7 +5073,6 @@ public abstract class BaseTest {
                     .ordinalPosition(0)
                     .name("location")
                     .internalName("location")
-                    .ordinalPosition(0)
                     .columnType(TableColumnType.VARCHAR)
                     .size(255L)
                     .isNullAllowed(false)
@@ -4834,7 +5083,6 @@ public abstract class BaseTest {
                     .ordinalPosition(1)
                     .name("lat")
                     .internalName("lat")
-                    .ordinalPosition(1)
                     .columnType(TableColumnType.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -4846,7 +5094,6 @@ public abstract class BaseTest {
                     .ordinalPosition(2)
                     .name("lng")
                     .internalName("lng")
-                    .ordinalPosition(2)
                     .columnType(TableColumnType.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -4863,11 +5110,11 @@ public abstract class BaseTest {
             .vdbid(VIEW_1_DATABASE_ID)
             .isPublic(VIEW_1_PUBLIC)
             .isSchemaPublic(VIEW_1_SCHEMA_PUBLIC)
+            .identifiers(null /* VIEW_1_DTO_IDENTIFIERS */)
             .owner(USER_1_BRIEF_DTO)
             .query(VIEW_1_QUERY)
             .queryHash(VIEW_1_QUERY_HASH)
             .columns(VIEW_1_COLUMNS_DTO)
-            .database(null)
             .build();
 
     public final static PrivilegedViewDto VIEW_1_PRIVILEGED_DTO = PrivilegedViewDto.builder()
@@ -4918,26 +5165,29 @@ public abstract class BaseTest {
     public final static List<ViewColumnDto> VIEW_2_COLUMNS_DTO = List.of(
             ViewColumnDto.builder()
                     .id(4L)
-                    .name("date")
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(0)
+                    .name("Date")
                     .internalName("date")
-                    .ordinalPosition(1)
                     .columnType(ColumnTypeDto.DATE)
                     .isNullAllowed(true)
                     .build(),
             ViewColumnDto.builder()
                     .id(5L)
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(1)
                     .name("loc")
                     .internalName("loc")
-                    .ordinalPosition(2)
                     .columnType(ColumnTypeDto.VARCHAR)
                     .size(255L)
                     .isNullAllowed(true)
                     .build(),
             ViewColumnDto.builder()
                     .id(6L)
-                    .name("mintemp")
-                    .internalName("mintemp")
-                    .ordinalPosition(3)
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(2)
+                    .name("Rainfall")
+                    .internalName("rainfall")
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -4945,9 +5195,10 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(7L)
-                    .name("rainfall")
-                    .internalName("rainfall")
-                    .ordinalPosition(4)
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(3)
+                    .name("MinTemp")
+                    .internalName("mintemp")
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -5073,9 +5324,10 @@ public abstract class BaseTest {
     public final static List<ViewColumnDto> VIEW_3_COLUMNS_DTO = List.of(
             ViewColumnDto.builder()
                     .id(8L)
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(0)
                     .name("MinTemp")
                     .internalName("mintemp")
-                    .ordinalPosition(0)
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -5083,30 +5335,31 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(9L)
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(1)
                     .name("Rainfall")
                     .internalName("rainfall")
-                    .ordinalPosition(1)
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
-                    .concept(CONCEPT_1_DTO)
-                    .unit(UNIT_1_DTO)
                     .isNullAllowed(true)
                     .build(),
             ViewColumnDto.builder()
                     .id(10L)
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(2)
                     .name("Location")
                     .internalName("location")
-                    .ordinalPosition(2)
                     .columnType(ColumnTypeDto.VARCHAR)
                     .size(255L)
                     .isNullAllowed(true)
                     .build(),
             ViewColumnDto.builder()
                     .id(11L)
+                    .databaseId(DATABASE_1_ID)
+                    .ordinalPosition(3)
                     .name("Date")
                     .internalName("date")
-                    .ordinalPosition(3)
                     .columnType(ColumnTypeDto.DATE)
                     .isNullAllowed(true)
                     .build()
@@ -5230,6 +5483,7 @@ public abstract class BaseTest {
     public final static List<ViewColumnDto> VIEW_4_COLUMNS_DTO = List.of(
             ViewColumnDto.builder()
                     .id(12L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(0)
                     .name("Animal Name")
                     .internalName("animal_name")
@@ -5238,6 +5492,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(13L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(1)
                     .name("Hair")
                     .internalName("hair")
@@ -5246,6 +5501,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(14L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(2)
                     .name("Feathers")
                     .internalName("feathers")
@@ -5254,6 +5510,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(15L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(3)
                     .name("Eggs")
                     .internalName("eggs")
@@ -5262,6 +5519,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(16L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(4)
                     .name("Milk")
                     .internalName("milk")
@@ -5270,6 +5528,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(17L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(5)
                     .name("Airborne")
                     .internalName("airborne")
@@ -5278,6 +5537,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(18L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(6)
                     .name("Aquantic")
                     .internalName("aquantic")
@@ -5286,6 +5546,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(19L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(7)
                     .name("Predator")
                     .internalName("predator")
@@ -5294,6 +5555,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(20L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(8)
                     .name("Backbone")
                     .internalName("backbone")
@@ -5302,6 +5564,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(21L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(9)
                     .name("Breathes")
                     .internalName("breathes")
@@ -5310,6 +5573,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(22L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(10)
                     .name("Venomous")
                     .internalName("venomous")
@@ -5318,6 +5582,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(23L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(11)
                     .name("Fin")
                     .internalName("fin")
@@ -5326,6 +5591,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(24L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(12)
                     .name("Legs")
                     .internalName("legs")
@@ -5334,6 +5600,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(25L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(13)
                     .name("Tail")
                     .internalName("tail")
@@ -5342,6 +5609,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(26L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(14)
                     .name("Domestic")
                     .internalName("domestic")
@@ -5350,6 +5618,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(27L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(15)
                     .name("Catsize")
                     .internalName("catsize")
@@ -5358,6 +5627,7 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(28L)
+                    .databaseId(DATABASE_2_ID)
                     .ordinalPosition(16)
                     .name("Class Type")
                     .internalName("class_type")
@@ -5620,7 +5890,6 @@ public abstract class BaseTest {
                     .ordinalPosition(0)
                     .name("location")
                     .internalName("location")
-                    .ordinalPosition(0)
                     .columnType(TableColumnType.VARCHAR)
                     .size(255L)
                     .isNullAllowed(false)
@@ -5631,7 +5900,6 @@ public abstract class BaseTest {
                     .ordinalPosition(1)
                     .name("lat")
                     .internalName("lat")
-                    .ordinalPosition(1)
                     .columnType(TableColumnType.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -5643,7 +5911,6 @@ public abstract class BaseTest {
                     .ordinalPosition(2)
                     .name("lng")
                     .internalName("lng")
-                    .ordinalPosition(2)
                     .columnType(TableColumnType.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -5654,20 +5921,20 @@ public abstract class BaseTest {
     public final static List<ViewColumnDto> VIEW_5_COLUMNS_DTO = List.of(
             ViewColumnDto.builder()
                     .id(29L)
+                    .databaseId(DATABASE_3_ID)
                     .ordinalPosition(0)
                     .name("location")
                     .internalName("location")
-                    .ordinalPosition(0)
                     .columnType(ColumnTypeDto.VARCHAR)
                     .size(255L)
                     .isNullAllowed(false)
                     .build(),
             ViewColumnDto.builder()
                     .id(30L)
+                    .databaseId(DATABASE_3_ID)
                     .ordinalPosition(1)
                     .name("lat")
                     .internalName("lat")
-                    .ordinalPosition(1)
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -5675,10 +5942,10 @@ public abstract class BaseTest {
                     .build(),
             ViewColumnDto.builder()
                     .id(31L)
+                    .databaseId(DATABASE_3_ID)
                     .ordinalPosition(2)
                     .name("lng")
                     .internalName("lng")
-                    .ordinalPosition(2)
                     .columnType(ColumnTypeDto.DECIMAL)
                     .size(10L)
                     .d(0L)
@@ -5769,6 +6036,8 @@ public abstract class BaseTest {
     public final static String CREATOR_3_ORCID = "00000-00000-00000";
     public final static String CREATOR_3_AFFIL = "TU Graz";
     public final static String CREATOR_3_AFFIL_ROR = "https://ror.org/04wn28048";
+    public final static AffiliationIdentifierSchemeType CREATOR_3_AFFIL_SCHEME_TYPE = AffiliationIdentifierSchemeType.ROR;
+    public final static AffiliationIdentifierSchemeTypeDto CREATOR_3_AFFIL_SCHEME_TYPE_DTO = AffiliationIdentifierSchemeTypeDto.ROR;
     public final static String CREATOR_3_AFFIL_URI = "https://ror.org/";
     public final static String CREATOR_3_FIRSTNAME = "Max";
     public final static String CREATOR_3_LASTNAME = "Mustermann";
@@ -6057,8 +6326,8 @@ public abstract class BaseTest {
     public final static Identifier IDENTIFIER_1_WITH_DOI = Identifier.builder()
             .id(IDENTIFIER_1_ID)
             .queryId(IDENTIFIER_1_QUERY_ID)
-            .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1))
-            .titles(List.of(IDENTIFIER_1_TITLE_1, IDENTIFIER_1_TITLE_2))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_1_DESCRIPTION_1)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_1_TITLE_1, IDENTIFIER_1_TITLE_2)))
             .doi(IDENTIFIER_1_DOI)
             .database(null /* for jpa */)
             .created(IDENTIFIER_1_CREATED)
@@ -6074,9 +6343,9 @@ public abstract class BaseTest {
             .publisher(IDENTIFIER_1_PUBLISHER)
             .type(IDENTIFIER_1_TYPE)
             .owner(USER_1)
-            .licenses(List.of(LICENSE_1))
-            .creators(List.of(IDENTIFIER_1_CREATOR_1))
-            .funders(List.of(IDENTIFIER_1_FUNDER_1))
+            .licenses(new LinkedList<>(List.of(LICENSE_1)))
+            .creators(new LinkedList<>(List.of(IDENTIFIER_1_CREATOR_1)))
+            .funders(new LinkedList<>(List.of(IDENTIFIER_1_FUNDER_1)))
             .status(IDENTIFIER_1_STATUS_TYPE)
             .build();
 
@@ -6084,8 +6353,8 @@ public abstract class BaseTest {
             .id(IDENTIFIER_1_ID)
             .databaseId(DATABASE_1_ID)
             .queryId(IDENTIFIER_1_QUERY_ID)
-            .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_DTO))
-            .titles(List.of(IDENTIFIER_1_TITLE_1_DTO, IDENTIFIER_1_TITLE_2_DTO))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_1_DESCRIPTION_1_DTO)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_1_TITLE_1_DTO, IDENTIFIER_1_TITLE_2_DTO)))
             .doi(IDENTIFIER_1_DOI)
             .execution(IDENTIFIER_1_EXECUTION)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
@@ -6097,10 +6366,10 @@ public abstract class BaseTest {
             .resultNumber(IDENTIFIER_1_RESULT_NUMBER)
             .publisher(IDENTIFIER_1_PUBLISHER)
             .type(IDENTIFIER_1_TYPE_DTO)
-            .owner(USER_1_DTO)
-            .licenses(List.of(LICENSE_1_DTO))
-            .creators(List.of(IDENTIFIER_1_CREATOR_1_DTO))
-            .funders(List.of(IDENTIFIER_1_FUNDER_1_DTO))
+            .owner(USER_1_BRIEF_DTO)
+            .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
+            .creators(new LinkedList<>(List.of(IDENTIFIER_1_CREATOR_1_DTO)))
+            .funders(new LinkedList<>(List.of(IDENTIFIER_1_FUNDER_1_DTO)))
             .status(IDENTIFIER_1_STATUS_TYPE_DTO)
             .build();
 
@@ -6108,7 +6377,7 @@ public abstract class BaseTest {
             .id(IDENTIFIER_1_ID)
             .databaseId(DATABASE_1_ID)
             .queryId(IDENTIFIER_1_QUERY_ID)
-            .titles(List.of(IDENTIFIER_1_TITLE_1_DTO, IDENTIFIER_1_TITLE_2_DTO))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_1_TITLE_1_DTO, IDENTIFIER_1_TITLE_2_DTO)))
             .doi(IDENTIFIER_1_DOI)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
             .publisher(IDENTIFIER_1_PUBLISHER)
@@ -6121,16 +6390,16 @@ public abstract class BaseTest {
             .type(IDENTIFIER_1_TYPE_DTO)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
             .publisher(IDENTIFIER_1_PUBLISHER)
-            .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
-            .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO, IDENTIFIER_1_TITLE_2_CREATE_DTO))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO, IDENTIFIER_1_TITLE_2_CREATE_DTO)))
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
             .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
             .publisher(IDENTIFIER_1_PUBLISHER)
             .type(IDENTIFIER_1_TYPE_DTO)
             .doi(IDENTIFIER_1_DOI)
-            .licenses(List.of(LICENSE_1_DTO))
-            .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
-            .funders(List.of(IDENTIFIER_1_FUNDER_1_CREATE_DTO))
+            .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
+            .creators(new LinkedList<>(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO)))
+            .funders(new LinkedList<>(List.of(IDENTIFIER_1_FUNDER_1_CREATE_DTO)))
             .build();
 
     public final static IdentifierCreateDto IDENTIFIER_1_CREATE_WITH_DOI_DTO = IdentifierCreateDto.builder()
@@ -6139,45 +6408,45 @@ public abstract class BaseTest {
             .doi(IDENTIFIER_1_DOI)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
             .publisher(IDENTIFIER_1_PUBLISHER)
-            .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
-            .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO, IDENTIFIER_1_TITLE_2_CREATE_DTO))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO, IDENTIFIER_1_TITLE_2_CREATE_DTO)))
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
             .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
             .publisher(IDENTIFIER_1_PUBLISHER)
             .type(IDENTIFIER_1_TYPE_DTO)
-            .licenses(List.of(LICENSE_1_DTO))
-            .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
-            .funders(List.of(IDENTIFIER_1_FUNDER_1_CREATE_DTO))
+            .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
+            .creators(new LinkedList<>(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO)))
+            .funders(new LinkedList<>(List.of(IDENTIFIER_1_FUNDER_1_CREATE_DTO)))
             .build();
 
     public final static IdentifierSaveDto IDENTIFIER_1_SAVE_DTO = IdentifierSaveDto.builder()
             .id(IDENTIFIER_1_ID)
             .databaseId(IDENTIFIER_1_DATABASE_ID)
-            .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
-            .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO, IDENTIFIER_1_TITLE_2_CREATE_DTO))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO, IDENTIFIER_1_TITLE_2_CREATE_DTO)))
             .relatedIdentifiers(new LinkedList<>())
             .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
-            .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
-            .funders(List.of(IDENTIFIER_1_FUNDER_1_CREATE_DTO))
+            .creators(new LinkedList<>(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO)))
+            .funders(new LinkedList<>(List.of(IDENTIFIER_1_FUNDER_1_CREATE_DTO)))
             .publisher(IDENTIFIER_1_PUBLISHER)
             .type(IDENTIFIER_1_TYPE_DTO)
-            .licenses(List.of(LICENSE_1_DTO))
+            .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
             .build();
 
     public final static IdentifierSaveDto IDENTIFIER_1_SAVE_MODIFY_DTO = IdentifierSaveDto.builder()
             .id(IDENTIFIER_1_ID)
             .databaseId(IDENTIFIER_1_DATABASE_ID)
-            .descriptions(List.of()) // <<<
-            .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO)) // <<<
+            .descriptions(new LinkedList<>(List.of())) // <<<
+            .titles(new LinkedList<>(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))) // <<<
             .relatedIdentifiers(new LinkedList<>())
             .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
-            .creators(List.of()) // <<<
-            .funders(List.of()) // <<<
+            .creators(new LinkedList<>(List.of())) // <<<
+            .funders(new LinkedList<>(List.of())) // <<<
             .publisher(IDENTIFIER_1_PUBLISHER)
             .type(IDENTIFIER_1_TYPE_DTO)
-            .licenses(List.of()) // <<<
+            .licenses(new LinkedList<>(List.of())) // <<<
             .build();
 
     public final static Long IDENTIFIER_5_ID = 5L;
@@ -6283,6 +6552,9 @@ public abstract class BaseTest {
             .nameIdentifier(CREATOR_1_ORCID)
             .nameIdentifierScheme(NameIdentifierSchemeTypeDto.ORCID)
             .affiliation(CREATOR_1_AFFIL)
+            .affiliationIdentifier(CREATOR_1_AFFIL_ROR)
+            .affiliationIdentifierScheme(CREATOR_1_AFFIL_TYPE_DTO)
+            .affiliationIdentifierSchemeUri(CREATOR_1_AFFIL_URI)
             .build();
 
     public final static CreatorSaveDto IDENTIFIER_5_CREATOR_1_CREATE_DTO = CreatorSaveDto.builder()
@@ -6372,8 +6644,8 @@ public abstract class BaseTest {
             .id(IDENTIFIER_5_ID)
             .databaseId(DATABASE_2_ID)
             .queryId(IDENTIFIER_5_QUERY_ID)
-            .descriptions(List.of(IDENTIFIER_5_DESCRIPTION_1_DTO))
-            .titles(List.of(IDENTIFIER_5_TITLE_1_DTO))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_5_DESCRIPTION_1_DTO)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_5_TITLE_1_DTO)))
             .doi(IDENTIFIER_5_DOI)
             .execution(IDENTIFIER_5_EXECUTION)
             .publicationDay(IDENTIFIER_5_PUBLICATION_DAY)
@@ -6386,15 +6658,16 @@ public abstract class BaseTest {
             .resultNumber(IDENTIFIER_5_RESULT_NUMBER)
             .publisher(IDENTIFIER_5_PUBLISHER)
             .type(IDENTIFIER_5_TYPE_DTO)
-            .owner(USER_2_DTO)
-            .creators(List.of(IDENTIFIER_5_CREATOR_1_DTO, IDENTIFIER_5_CREATOR_2_DTO))
+            .owner(USER_2_BRIEF_DTO)
+            .status(IDENTIFIER_5_STATUS_TYPE_DTO)
+            .creators(new LinkedList<>(List.of(IDENTIFIER_5_CREATOR_1_DTO, IDENTIFIER_5_CREATOR_2_DTO)))
             .build();
 
     public final static IdentifierBriefDto IDENTIFIER_5_BRIEF_DTO = IdentifierBriefDto.builder()
             .id(IDENTIFIER_5_ID)
             .databaseId(DATABASE_2_ID)
             .queryId(IDENTIFIER_5_QUERY_ID)
-            .titles(List.of(IDENTIFIER_5_TITLE_1_DTO))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_5_TITLE_1_DTO)))
             .doi(IDENTIFIER_5_DOI)
             .publicationYear(IDENTIFIER_5_PUBLICATION_YEAR)
             .publisher(IDENTIFIER_5_PUBLISHER)
@@ -6433,15 +6706,15 @@ public abstract class BaseTest {
             .id(IDENTIFIER_5_ID)
             .queryId(IDENTIFIER_5_QUERY_ID)
             .databaseId(IDENTIFIER_5_DATABASE_ID)
-            .descriptions(List.of(IDENTIFIER_5_DESCRIPTION_1_CREATE_DTO))
-            .titles(List.of(IDENTIFIER_5_TITLE_1_CREATE_DTO))
-            .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_5_DESCRIPTION_1_CREATE_DTO)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_5_TITLE_1_CREATE_DTO)))
+            .relatedIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO)))
             .publicationDay(IDENTIFIER_5_PUBLICATION_DAY)
             .publicationMonth(IDENTIFIER_5_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_5_PUBLICATION_YEAR)
-            .creators(List.of(IDENTIFIER_5_CREATOR_1_CREATE_DTO, IDENTIFIER_5_CREATOR_2_CREATE_DTO))
+            .creators(new LinkedList<>(List.of(IDENTIFIER_5_CREATOR_1_CREATE_DTO, IDENTIFIER_5_CREATOR_2_CREATE_DTO)))
             .publisher(IDENTIFIER_5_PUBLISHER)
-            .licenses(List.of(LICENSE_1_DTO))
+            .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
             .type(IDENTIFIER_5_TYPE_DTO)
             .build();
 
@@ -6540,6 +6813,7 @@ public abstract class BaseTest {
             .affiliation(CREATOR_1_AFFIL)
             .affiliationIdentifier(CREATOR_1_AFFIL_ROR)
             .affiliationIdentifierScheme(CREATOR_1_AFFIL_TYPE)
+            .affiliationIdentifierSchemeUri(CREATOR_1_AFFIL_URI)
             .build();
 
     public final static CreatorDto IDENTIFIER_6_CREATOR_1_DTO = CreatorDto.builder()
@@ -6608,7 +6882,10 @@ public abstract class BaseTest {
             .creatorName(CREATOR_3_NAME)
             .nameIdentifier(CREATOR_3_ORCID)
             .nameIdentifierScheme(NameIdentifierSchemeType.ORCID)
+            .affiliation(CREATOR_3_AFFIL)
             .affiliationIdentifier(CREATOR_3_AFFIL_ROR)
+            .affiliationIdentifierScheme(CREATOR_3_AFFIL_SCHEME_TYPE)
+            .affiliationIdentifierSchemeUri(CREATOR_3_AFFIL_URI)
             .build();
 
     public final static CreatorDto IDENTIFIER_6_CREATOR_3_DTO = CreatorDto.builder()
@@ -6620,6 +6897,8 @@ public abstract class BaseTest {
             .nameIdentifierScheme(NameIdentifierSchemeTypeDto.ORCID)
             .affiliation(CREATOR_3_AFFIL)
             .affiliationIdentifier(CREATOR_3_AFFIL_ROR)
+            .affiliationIdentifierScheme(CREATOR_3_AFFIL_SCHEME_TYPE_DTO)
+            .affiliationIdentifierSchemeUri(CREATOR_3_AFFIL_URI)
             .build();
 
     public final static Identifier IDENTIFIER_6 = Identifier.builder()
@@ -6652,8 +6931,8 @@ public abstract class BaseTest {
             .id(IDENTIFIER_6_ID)
             .databaseId(DATABASE_3_ID)
             .queryId(IDENTIFIER_6_QUERY_ID)
-            .descriptions(List.of(IDENTIFIER_6_DESCRIPTION_1_DTO))
-            .titles(List.of(IDENTIFIER_6_TITLE_1_DTO))
+            .descriptions(new LinkedList<>(List.of(IDENTIFIER_6_DESCRIPTION_1_DTO)))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_6_TITLE_1_DTO)))
             .doi(IDENTIFIER_6_DOI)
             .execution(IDENTIFIER_6_EXECUTION)
             .publicationDay(IDENTIFIER_6_PUBLICATION_DAY)
@@ -6666,7 +6945,7 @@ public abstract class BaseTest {
             .resultNumber(IDENTIFIER_6_RESULT_NUMBER)
             .publisher(IDENTIFIER_6_PUBLISHER)
             .type(IDENTIFIER_6_TYPE_DTO)
-            .owner(USER_3_DTO)
+            .owner(USER_3_BRIEF_DTO)
             .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
             .creators(new LinkedList<>(List.of(IDENTIFIER_6_CREATOR_1_DTO, IDENTIFIER_6_CREATOR_2_DTO, IDENTIFIER_6_CREATOR_3_DTO)))
             .status(IDENTIFIER_6_STATUS_TYPE_DTO)
@@ -6677,7 +6956,7 @@ public abstract class BaseTest {
             .id(IDENTIFIER_6_ID)
             .databaseId(DATABASE_3_ID)
             .queryId(IDENTIFIER_6_QUERY_ID)
-            .titles(List.of(IDENTIFIER_6_TITLE_1_DTO))
+            .titles(new LinkedList<>(List.of(IDENTIFIER_6_TITLE_1_DTO)))
             .doi(IDENTIFIER_6_DOI)
             .publicationYear(IDENTIFIER_6_PUBLICATION_YEAR)
             .publisher(IDENTIFIER_6_PUBLISHER)
@@ -6772,10 +7051,11 @@ public abstract class BaseTest {
             .resultNumber(IDENTIFIER_7_RESULT_NUMBER)
             .publisher(IDENTIFIER_7_PUBLISHER)
             .type(IDENTIFIER_7_TYPE_DTO)
-            .owner(USER_4_DTO)
+            .owner(USER_4_BRIEF_DTO)
+            .relatedIdentifiers(new LinkedList<>())
             .licenses(new LinkedList<>())
             .funders(new LinkedList<>())
-            .creators(new LinkedList<>())
+            .creators(new LinkedList<>(List.of(IDENTIFIER_7_CREATOR_1_DTO)))
             .status(IDENTIFIER_7_STATUS_TYPE_DTO)
             .build();
 
@@ -6803,7 +7083,7 @@ public abstract class BaseTest {
             .relatedIdentifiers(new LinkedList<>())
             .publicationMonth(IDENTIFIER_7_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_7_PUBLICATION_YEAR)
-            .creators(List.of(IDENTIFIER_7_CREATOR_1_CREATE_DTO))
+            .creators(new LinkedList<>(List.of(IDENTIFIER_7_CREATOR_1_CREATE_DTO)))
             .funders(new LinkedList<>())
             .licenses(new LinkedList<>())
             .publisher(IDENTIFIER_7_PUBLISHER)
@@ -6885,7 +7165,7 @@ public abstract class BaseTest {
             .resultNumber(IDENTIFIER_2_RESULT_NUMBER)
             .publisher(IDENTIFIER_2_PUBLISHER)
             .type(IDENTIFIER_2_TYPE_DTO)
-            .owner(USER_1_DTO)
+            .owner(USER_1_BRIEF_DTO)
             .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
             .creators(new LinkedList<>())
             .status(IDENTIFIER_2_STATUS_TYPE_DTO)
@@ -6915,7 +7195,7 @@ public abstract class BaseTest {
             .creators(new LinkedList<>())
             .publisher(IDENTIFIER_2_PUBLISHER)
             .type(IDENTIFIER_2_TYPE_DTO)
-            .licenses(List.of(LICENSE_1_DTO))
+            .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
             .queryId(QUERY_1_ID)
             .build();
 
@@ -6986,7 +7266,7 @@ public abstract class BaseTest {
             .resultNumber(IDENTIFIER_3_RESULT_NUMBER)
             .publisher(IDENTIFIER_3_PUBLISHER)
             .type(IDENTIFIER_3_TYPE_DTO)
-            .owner(USER_1_DTO)
+            .owner(USER_1_BRIEF_DTO)
             .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
             .creators(new LinkedList<>())
             .status(IDENTIFIER_3_STATUS_TYPE_DTO)
@@ -7085,7 +7365,7 @@ public abstract class BaseTest {
             .resultNumber(IDENTIFIER_4_RESULT_NUMBER)
             .publisher(IDENTIFIER_4_PUBLISHER)
             .type(IDENTIFIER_4_TYPE_DTO)
-            .owner(USER_1_DTO)
+            .owner(USER_1_BRIEF_DTO)
             .licenses(new LinkedList<>(List.of(LICENSE_1_DTO)))
             .creators(new LinkedList<>())
             .status(IDENTIFIER_4_STATUS_TYPE_DTO)
@@ -7161,6 +7441,14 @@ public abstract class BaseTest {
             .displayEnd(BANNER_MESSAGE_1_END)
             .build();
 
+    public final static BannerMessageDto BANNER_MESSAGE_1_DTO = BannerMessageDto.builder()
+            .id(BANNER_MESSAGE_1_ID)
+            .message(BANNER_MESSAGE_1_MESSAGE)
+            .type(BANNER_MESSAGE_1_TYPE_DTO)
+            .displayStart(BANNER_MESSAGE_1_START)
+            .displayEnd(BANNER_MESSAGE_1_END)
+            .build();
+
     public final static BannerMessageCreateDto BANNER_MESSAGE_1_CREATE_DTO = BannerMessageCreateDto.builder()
             .message(BANNER_MESSAGE_1_MESSAGE)
             .type(BANNER_MESSAGE_1_TYPE_DTO)
@@ -7205,7 +7493,7 @@ public abstract class BaseTest {
             .isSchemaPublic(DATABASE_1_SCHEMA_PUBLIC)
             .name(DATABASE_1_NAME)
             .description(DATABASE_1_DESCRIPTION)
-            .identifiers(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4))
+            .identifiers(new LinkedList<>(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4)))
             .cid(CONTAINER_1_ID)
             .container(CONTAINER_1)
             .internalName(DATABASE_1_INTERNALNAME)
@@ -7233,21 +7521,22 @@ public abstract class BaseTest {
             .container(CONTAINER_1_BRIEF_DTO)
             .internalName(DATABASE_1_INTERNALNAME)
             .exchangeName(DATABASE_1_EXCHANGE)
-            .identifiers(List.of(IDENTIFIER_1_BRIEF_DTO, IDENTIFIER_2_BRIEF_DTO, IDENTIFIER_3_BRIEF_DTO, IDENTIFIER_4_BRIEF_DTO))
-            .tables(List.of(TABLE_1_BRIEF_DTO, TABLE_2_BRIEF_DTO, TABLE_3_BRIEF_DTO, TABLE_4_BRIEF_DTO))
-            .views(List.of(VIEW_1_BRIEF_DTO, VIEW_2_BRIEF_DTO, VIEW_3_BRIEF_DTO))
+            .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_BRIEF_DTO, IDENTIFIER_2_BRIEF_DTO, IDENTIFIER_3_BRIEF_DTO, IDENTIFIER_4_BRIEF_DTO)))
+            .tables(new LinkedList<>(List.of(TABLE_1_BRIEF_DTO, TABLE_2_BRIEF_DTO, TABLE_3_BRIEF_DTO, TABLE_4_BRIEF_DTO)))
+            .views(new LinkedList<>(List.of(VIEW_1_BRIEF_DTO, VIEW_2_BRIEF_DTO, VIEW_3_BRIEF_DTO)))
             .build();
 
     public final static PrivilegedDatabaseDto DATABASE_1_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder()
             .id(DATABASE_1_ID)
             .isPublic(DATABASE_1_PUBLIC)
+            .isSchemaPublic(DATABASE_1_SCHEMA_PUBLIC)
             .name(DATABASE_1_NAME)
             .container(CONTAINER_1_PRIVILEGED_DTO)
             .internalName(DATABASE_1_INTERNALNAME)
             .exchangeName(DATABASE_1_EXCHANGE)
-            .identifiers(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))
-            .tables(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))
-            .views(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))
+            .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO)))
+            .tables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO)))
+            .views(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO)))
             .owner(USER_1_BRIEF_DTO)
             .lastRetrieved(Instant.now())
             .build();
@@ -7386,13 +7675,14 @@ public abstract class BaseTest {
     public final static PrivilegedDatabaseDto DATABASE_2_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder()
             .id(DATABASE_2_ID)
             .isPublic(DATABASE_2_PUBLIC)
+            .isSchemaPublic(DATABASE_2_SCHEMA_PUBLIC)
             .name(DATABASE_2_NAME)
             .container(CONTAINER_1_PRIVILEGED_DTO)
             .internalName(DATABASE_2_INTERNALNAME)
             .exchangeName(DATABASE_2_EXCHANGE)
-            .identifiers(List.of(IDENTIFIER_5_DTO))
-            .tables(List.of(TABLE_5_DTO, TABLE_6_DTO, TABLE_7_DTO))
-            .views(List.of(VIEW_4_DTO))
+            .identifiers(new LinkedList<>(List.of(IDENTIFIER_5_DTO)))
+            .tables(new LinkedList<>(List.of(TABLE_5_DTO, TABLE_6_DTO, TABLE_7_DTO)))
+            .views(new LinkedList<>(List.of(VIEW_4_DTO)))
             .owner(USER_2_BRIEF_DTO)
             .lastRetrieved(Instant.now())
             .build();
@@ -7404,9 +7694,9 @@ public abstract class BaseTest {
             .container(CONTAINER_1_BRIEF_DTO)
             .internalName(DATABASE_2_INTERNALNAME)
             .exchangeName(DATABASE_2_EXCHANGE)
-            .identifiers(List.of(IDENTIFIER_5_BRIEF_DTO))
-            .tables(List.of(TABLE_5_BRIEF_DTO, TABLE_6_BRIEF_DTO, TABLE_7_BRIEF_DTO))
-            .views(List.of(VIEW_4_BRIEF_DTO))
+            .identifiers(new LinkedList<>(List.of(IDENTIFIER_5_BRIEF_DTO)))
+            .tables(new LinkedList<>(List.of(TABLE_5_BRIEF_DTO, TABLE_6_BRIEF_DTO, TABLE_7_BRIEF_DTO)))
+            .views(new LinkedList<>(List.of(VIEW_4_BRIEF_DTO)))
             .identifiers(new LinkedList<>())
             .owner(USER_2_BRIEF_DTO)
             .build();
@@ -7524,7 +7814,7 @@ public abstract class BaseTest {
             .tables(new LinkedList<>())
             .views(new LinkedList<>())
             .accesses(new LinkedList<>()) /* DATABASE_3_USER_1_WRITE_ALL_ACCESS */
-            .identifiers(new LinkedList<>())
+            .identifiers(new LinkedList<>()) /* IDENTIFIER_6 */
             .build();
 
     public final static DatabaseAccess DATABASE_3_USER_1_READ_ACCESS = DatabaseAccess.builder()
@@ -7637,13 +7927,14 @@ public abstract class BaseTest {
     public final static PrivilegedDatabaseDto DATABASE_3_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder()
             .id(DATABASE_3_ID)
             .isPublic(DATABASE_3_PUBLIC)
+            .isSchemaPublic(DATABASE_3_SCHEMA_PUBLIC)
             .name(DATABASE_3_NAME)
             .container(CONTAINER_1_PRIVILEGED_DTO)
             .internalName(DATABASE_3_INTERNALNAME)
             .exchangeName(DATABASE_3_EXCHANGE)
-            .identifiers(List.of(IDENTIFIER_6_DTO))
-            .tables(List.of(TABLE_8_DTO))
-            .views(List.of(VIEW_5_DTO))
+            .identifiers(new LinkedList<>(List.of(IDENTIFIER_6_DTO)))
+            .tables(new LinkedList<>(List.of(TABLE_8_DTO)))
+            .views(new LinkedList<>(List.of(VIEW_5_DTO)))
             .owner(USER_3_BRIEF_DTO)
             .lastRetrieved(Instant.now())
             .build();
@@ -7694,6 +7985,21 @@ public abstract class BaseTest {
             .identifiers(new LinkedList<>())
             .build();
 
+    public final static PrivilegedDatabaseDto DATABASE_4_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder()
+            .id(DATABASE_4_ID)
+            .isPublic(DATABASE_4_PUBLIC)
+            .isSchemaPublic(DATABASE_4_SCHEMA_PUBLIC)
+            .name(DATABASE_4_NAME)
+            .container(CONTAINER_1_PRIVILEGED_DTO)
+            .internalName(DATABASE_4_INTERNALNAME)
+            .exchangeName(DATABASE_4_EXCHANGE)
+            .identifiers(new LinkedList<>(List.of(IDENTIFIER_7_DTO)))
+            .tables(new LinkedList<>(List.of(TABLE_9_DTO)))
+            .views(new LinkedList<>(List.of()))
+            .owner(USER_3_BRIEF_DTO)
+            .lastRetrieved(Instant.now())
+            .build();
+
     public final static DatabaseAccess DATABASE_4_USER_1_READ_ACCESS = DatabaseAccess.builder()
             .type(AccessType.READ)
             .hdbid(DATABASE_4_ID)
@@ -7757,6 +8063,8 @@ public abstract class BaseTest {
             .huserid(USER_3_ID)
             .build();
 
+    public final static List<IdentifierDto> VIEW_1_DTO_IDENTIFIERS = List.of(IDENTIFIER_3_DTO);
+
     public final static Constraints TABLE_1_CONSTRAINTS = Constraints.builder()
             .checks(new LinkedHashSet<>())
             .foreignKeys(new LinkedList<>())
@@ -7928,6 +8236,7 @@ public abstract class BaseTest {
     public final static Constraints TABLE_7_CONSTRAINTS = Constraints.builder()
             .checks(new LinkedHashSet<>())
             .foreignKeys(new LinkedList<>(List.of(ForeignKey.builder()
+                            .id(8L)
                             .name("fk_name_id")
                             .onDelete(ReferenceType.NO_ACTION)
                             .references(new LinkedList<>(List.of(ForeignKeyReference.builder()
@@ -7941,6 +8250,7 @@ public abstract class BaseTest {
                             .onUpdate(ReferenceType.NO_ACTION)
                             .build(),
                     ForeignKey.builder()
+                            .id(9L)
                             .name("fk_zoo_id")
                             .onDelete(ReferenceType.NO_ACTION)
                             .references(new LinkedList<>(List.of(ForeignKeyReference.builder()
diff --git a/dbrepo-search-service/Pipfile b/dbrepo-search-service/Pipfile
index 287670f8ddac63952943a27dcdddbe08e78289bc..ec74a381be4f01297cf01d2f638182ba2a8a1d9d 100644
--- a/dbrepo-search-service/Pipfile
+++ b/dbrepo-search-service/Pipfile
@@ -18,7 +18,7 @@ jwt = "~=1.3"
 testcontainers-opensearch = "*"
 pytest = "*"
 rdflib = "*"
-dbrepo = {path = "./lib/dbrepo-1.6.0.tar.gz"}
+dbrepo = {path = "./lib/dbrepo-1.6.1.tar.gz"}
 gunicorn = "*"
 
 [dev-packages]
diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock
index c7e1f6005d78d40641abae50f4bb3189251aac42..c0508dd3daf66ff03c848411ae47f1698da81014 100644
--- a/dbrepo-search-service/Pipfile.lock
+++ b/dbrepo-search-service/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "ed3c0ae802499c4020eea0c4d2e62f57a0a642cc0ebe9698f228cd8fd8f42ccb"
+            "sha256": "a0682b0583cfc91d643a307a7dce7a524e7f7c29dbf2c9c5e9a6f16eb5f5ee91"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -360,9 +360,9 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:769c6d9c4475b26d3f752dcc9910346798d31afeca45ef014f48dd293074b8fb"
+                "sha256:a08b6eb49c108466b231c1b2cae5be501043fe4208a782899ce103105e22e3c6"
             ],
-            "path": "./lib/dbrepo-1.6.0.tar.gz"
+            "path": "./lib/dbrepo-1.6.1.tar.gz"
         },
         "docker": {
             "hashes": [
@@ -602,7 +602,7 @@
                 "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79",
                 "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"
             ],
-            "markers": "python_version < '3.13' and (platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32'))))))",
+            "markers": "python_version < '3.14' and (platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32'))))))",
             "version": "==3.1.1"
         },
         "gunicorn": {
@@ -1099,11 +1099,11 @@
         },
         "pydantic": {
             "hashes": [
-                "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d",
-                "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"
+                "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff",
+                "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==2.10.4"
+            "version": "==2.10.5"
         },
         "pydantic-core": {
             "hashes": [
@@ -1321,20 +1321,20 @@
         },
         "rdflib": {
             "hashes": [
-                "sha256:164de86bd3564558802ca983d84f6616a4a1a420c7a17a8152f5016076b2913e",
-                "sha256:e590fa9a2c34ba33a667818b5a84be3fb8a4d85868f8038f17912ec84f912a25"
+                "sha256:4fc8f6d50b199dc38fbc5256370f038c1cedca6102ccbde4e37c0fd2b7f36e65",
+                "sha256:5a694a64f48a751079999c37dccf91a6210077d845d09adf7c3ce23a876265a7"
             ],
             "index": "pypi",
-            "markers": "python_full_version >= '3.8.1' and python_full_version < '4.0.0'",
-            "version": "==7.1.1"
+            "markers": "python_version >= '3.9' and python_version < '4'",
+            "version": "==7.1.2"
         },
         "referencing": {
             "hashes": [
-                "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c",
-                "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"
+                "sha256:363d9c65f080d0d70bc41c721dce3c7f3e77fc09f269cd5c8813da18069a6794",
+                "sha256:ca2e6492769e3602957e9b831b94211599d2aade9477f5d44110d2530cf9aade"
             ],
-            "markers": "python_version >= '3.8'",
-            "version": "==0.35.1"
+            "markers": "python_version >= '3.9'",
+            "version": "==0.36.1"
         },
         "requests": {
             "hashes": [
@@ -1463,66 +1463,66 @@
         },
         "sqlalchemy": {
             "hashes": [
-                "sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763",
-                "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436",
-                "sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2",
-                "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588",
-                "sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e",
-                "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959",
-                "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d",
-                "sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575",
-                "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908",
-                "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8",
-                "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8",
-                "sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545",
-                "sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7",
-                "sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971",
-                "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855",
-                "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c",
-                "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71",
-                "sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d",
-                "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb",
-                "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72",
-                "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f",
-                "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5",
-                "sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346",
-                "sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24",
-                "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e",
-                "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5",
-                "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08",
-                "sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793",
-                "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88",
-                "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686",
-                "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b",
-                "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2",
-                "sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28",
-                "sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d",
-                "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5",
-                "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a",
-                "sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a",
-                "sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3",
-                "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf",
-                "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5",
-                "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef",
-                "sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689",
-                "sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c",
-                "sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b",
-                "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07",
-                "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa",
-                "sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06",
-                "sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1",
-                "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff",
-                "sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa",
-                "sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687",
-                "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4",
-                "sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb",
-                "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44",
-                "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c",
-                "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e",
-                "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53"
+                "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278",
+                "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6",
+                "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb",
+                "sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098",
+                "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd",
+                "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2",
+                "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef",
+                "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94",
+                "sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b",
+                "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4",
+                "sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b",
+                "sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84",
+                "sha256:44f569d0b1eb82301b92b72085583277316e7367e038d97c3a1a899d9a05e342",
+                "sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989",
+                "sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b",
+                "sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b",
+                "sha256:51bc9cfef83e0ac84f86bf2b10eaccb27c5a3e66a1212bef676f5bee6ef33ebb",
+                "sha256:521ef85c04c33009166777c77e76c8a676e2d8528dc83a57836b63ca9c69dcd1",
+                "sha256:5bc3339db84c5fb9130ac0e2f20347ee77b5dd2596ba327ce0d399752f4fce39",
+                "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff",
+                "sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33",
+                "sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72",
+                "sha256:6b788f14c5bb91db7f468dcf76f8b64423660a05e57fe277d3f4fad7b9dcb7ce",
+                "sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4",
+                "sha256:74bbd1d0a9bacf34266a7907d43260c8d65d31d691bb2356f41b17c2dca5b1d0",
+                "sha256:75311559f5c9881a9808eadbeb20ed8d8ba3f7225bef3afed2000c2a9f4d49b9",
+                "sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c",
+                "sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba",
+                "sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a",
+                "sha256:84b9f23b0fa98a6a4b99d73989350a94e4a4ec476b9a7dfe9b79ba5939f5e80b",
+                "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658",
+                "sha256:8e47f1af09444f87c67b4f1bb6231e12ba6d4d9f03050d7fc88df6d075231a49",
+                "sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636",
+                "sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9",
+                "sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8",
+                "sha256:9d087663b7e1feabea8c578d6887d59bb00388158e8bff3a76be11aa3f748ca2",
+                "sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087",
+                "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1",
+                "sha256:b2eae3423e538c10d93ae3e87788c6a84658c3ed6db62e6a61bb9495b0ad16bb",
+                "sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5",
+                "sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f",
+                "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0",
+                "sha256:cce918ada64c956b62ca2c2af59b125767097ec1dca89650a6221e887521bfd7",
+                "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb",
+                "sha256:cf95a60b36997dad99692314c4713f141b61c5b0b4cc5c3426faad570b31ca01",
+                "sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8",
+                "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2",
+                "sha256:d75ead7dd4d255068ea0f21492ee67937bd7c90964c8f3c2bea83c7b7f81b95f",
+                "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e",
+                "sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9",
+                "sha256:dfff7be361048244c3aa0f60b5e63221c5e0f0e509f4e47b8910e22b57d10ae7",
+                "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb",
+                "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4",
+                "sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1",
+                "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069",
+                "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761",
+                "sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==2.0.36"
+            "version": "==2.0.37"
         },
         "sqlalchemy-utils": {
             "hashes": [
@@ -1598,74 +1598,88 @@
         },
         "wrapt": {
             "hashes": [
-                "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d",
-                "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301",
-                "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635",
-                "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a",
-                "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed",
-                "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721",
-                "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801",
-                "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b",
-                "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1",
-                "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88",
-                "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8",
-                "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0",
-                "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f",
-                "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578",
-                "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7",
-                "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045",
-                "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada",
-                "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d",
-                "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b",
-                "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a",
-                "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977",
-                "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea",
-                "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346",
-                "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13",
-                "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22",
-                "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339",
-                "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9",
-                "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181",
-                "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c",
-                "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90",
-                "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a",
-                "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489",
-                "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f",
-                "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504",
-                "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea",
-                "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569",
-                "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4",
-                "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce",
-                "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab",
-                "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a",
-                "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f",
-                "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c",
-                "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9",
-                "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf",
-                "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d",
-                "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627",
-                "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d",
-                "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4",
-                "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c",
-                "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d",
-                "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad",
-                "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b",
-                "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33",
-                "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371",
-                "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1",
-                "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393",
-                "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106",
-                "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df",
-                "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379",
-                "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451",
-                "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b",
-                "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575",
-                "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed",
-                "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb",
-                "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"
+                "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f",
+                "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c",
+                "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a",
+                "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b",
+                "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555",
+                "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c",
+                "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b",
+                "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6",
+                "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8",
+                "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662",
+                "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061",
+                "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998",
+                "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb",
+                "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62",
+                "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984",
+                "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392",
+                "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2",
+                "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306",
+                "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7",
+                "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3",
+                "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9",
+                "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6",
+                "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192",
+                "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317",
+                "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f",
+                "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda",
+                "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563",
+                "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a",
+                "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f",
+                "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d",
+                "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9",
+                "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8",
+                "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82",
+                "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9",
+                "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845",
+                "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82",
+                "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125",
+                "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504",
+                "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b",
+                "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7",
+                "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc",
+                "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6",
+                "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40",
+                "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a",
+                "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3",
+                "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a",
+                "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72",
+                "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681",
+                "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438",
+                "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae",
+                "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2",
+                "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb",
+                "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5",
+                "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a",
+                "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3",
+                "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8",
+                "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2",
+                "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22",
+                "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72",
+                "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061",
+                "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f",
+                "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9",
+                "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04",
+                "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98",
+                "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9",
+                "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f",
+                "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b",
+                "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925",
+                "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6",
+                "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0",
+                "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9",
+                "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c",
+                "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991",
+                "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6",
+                "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000",
+                "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb",
+                "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119",
+                "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b",
+                "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==1.17.0"
+            "version": "==1.17.2"
         },
         "yarl": {
             "hashes": [
diff --git a/dbrepo-search-service/app.py b/dbrepo-search-service/app.py
index e4e8581a9ee484c198f5a00a76a58b5296dd4188..f9e2dbcc77ac11a6f7c8175e2b0d868f848c98af 100644
--- a/dbrepo-search-service/app.py
+++ b/dbrepo-search-service/app.py
@@ -5,17 +5,19 @@ from json import dumps
 from typing import List, Any
 
 import requests
-from clients.keycloak_client import User, KeycloakClient
-from clients.opensearch_client import OpenSearchClient
 from dbrepo.api.dto import Database, ApiError
 from flasgger import LazyJSONEncoder, Swagger, swag_from
-from flask import Flask, request
+from flask import Flask, request, Response
 from flask_cors import CORS
 from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth
 from jwt.exceptions import JWTDecodeError
 from opensearchpy import NotFoundError
 from prometheus_flask_exporter import PrometheusMetrics
 from pydantic import ValidationError
+from pydantic.deprecated.json import pydantic_encoder
+
+from clients.keycloak_client import User, KeycloakClient
+from clients.opensearch_client import OpenSearchClient, flatten
 
 logging.addLevelName(level=logging.NOTSET, levelName='TRACE')
 logging.basicConfig(level=logging.DEBUG)
@@ -123,18 +125,6 @@ template = {
                     }
                 }
             },
-            "SearchResultDto": {
-                "required": ["results"],
-                "type": "object",
-                "properties": {
-                    "results": {
-                        "type": "array",
-                        "items": {
-                            "type": "object"
-                        }
-                    }
-                }
-            },
             "SearchRequestDto": {
                 "required": ["search_term", "field_value_pairs"],
                 "type": "object",
@@ -208,13 +198,13 @@ app.json_encoder = LazyJSONEncoder
 
 
 @token_auth.verify_token
-def verify_token(token: str):
+def verify_token(token: str) -> bool | User:
     if token is None or token == "":
         return False
     try:
         client = KeycloakClient()
         return client.verify_jwt(access_token=token)
-    except JWTDecodeError as error:
+    except JWTDecodeError:
         return False
 
 
@@ -259,10 +249,10 @@ def general_filter(index, results):
         "table": ["id", "name", "description"],
         "identifier": ["id", "type", "creator"],
         "user": ["id", "username"],
-        "database": ["id", "name", "is_public", "details"],
+        "database": ["id", "name", "is_public", "is_schema_public", "details"],
         "concept": ["uri", "name"],
         "unit": [],
-        "view": ["id", "name", "creator", " created"],
+        "view": ["id", "name", "creator"],
     }
     if index not in important_keys.keys():
         raise KeyError(f"Failed to find index {index} in: {important_keys.keys()}")
@@ -289,7 +279,7 @@ def get_index(index: str):
     :param index: desired index
     :return: list of the results
     """
-    logging.info(f'Searching for index: {index}')
+    logging.debug(f'endpoint get search type: {index}')
     results = OpenSearchClient().query_index_by_term_opensearch("*", "contains")
     try:
         results = general_filter(index, results)
@@ -298,7 +288,7 @@ def get_index(index: str):
         max_pages = math.ceil(len(results) / results_per_page)
         page = min(request.args.get("page", 1, type=int), max_pages)
         results = results[(results_per_page * (page - 1)): (results_per_page * page)]
-        return dict({"results": results}), 200
+        return Response(dumps(results, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
     except KeyError:
         return ApiError(status='NOT_FOUND', message=f'Failed to find get index: {index}',
                         code='search.index.missing').model_dump(), 404
@@ -313,11 +303,11 @@ def get_fields(field_type: str):
     :param field_type: The search type
     :return:
     """
-    logging.info(f'Searching in index database for type: {field_type}')
+    logging.debug(f'endpoint get search type fields: {field_type}')
     try:
         fields = OpenSearchClient().get_fields_for_index(field_type)
         logging.debug(f'get fields for field_type {field_type} resulted in {len(fields)} field(s)')
-        return fields, 200
+        return Response(dumps(fields, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
     except NotFoundError:
         return ApiError(status='NOT_FOUND', message=f'Failed to find fields for search type {field_type}',
                         code='search.type.missing').model_dump(), 404
@@ -331,15 +321,19 @@ def get_fuzzy_search():
     Main endpoint for fuzzy searching.
     :return:
     """
-    search_term: str = request.args.get('q')
+    search_term: str | None = request.args.get('q')
+    logging.debug(f'endpoint get fuzzy search, q={search_term}')
     if search_term is None or len(search_term) == 0:
         return ApiError(status='BAD_REQUEST', message='Provide a search term with ?q=term',
                         code='search.fuzzy.invalid').model_dump(), 400
     logging.debug(f"search request query: {search_term}")
-    results = OpenSearchClient().fuzzy_search(search_term)
-    if "hits" in results and "hits" in results["hits"]:
-        results = [hit["_source"] for hit in results["hits"]["hits"]]
-    return dict({"results": results}), 200
+    user_id, error, status = KeycloakClient().userId(request.headers.get('Authorization'))
+    if error is not None and status is not None:
+        return error, status
+    results: [Database] = OpenSearchClient().fuzzy_search(search_term=search_term,
+                                                          user_id=user_id,
+                                                          user_token=request.headers.get('Authorization'))
+    return Response(dumps(results, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
 
 
 @app.route("/api/search/<string:field_type>", methods=["POST"], endpoint="search_post_general_search")
@@ -353,27 +347,32 @@ def post_general_search(field_type):
     if request.content_type != "application/json":
         return ApiError(status='UNSUPPORTED_MEDIA_TYPE', message='Content type needs to be application/json',
                         code='search.general.media').model_dump(), 415
-    req_body = request.json
-    logging.info(f'Searching in index database for type: {field_type}')
+    value_pairs = request.json
+    logging.debug(f'endpoint get general search, field_type={field_type}, value_pairs={value_pairs}')
     t1 = request.args.get("t1")
     if not str(t1).isdigit():
         t1 = None
     t2 = request.args.get("t2")
     if not str(t2).isdigit():
         t2 = None
-    if t1 is not None and t2 is not None and "unit.uri" in req_body and "concept.uri" in req_body:
-        response = OpenSearchClient().unit_independent_search(t1, t2, req_body)
+    user_id, error, status = KeycloakClient().userId(request.headers.get('Authorization'))
+    if error is not None and status is not None:
+        return error, status
+    if t1 is not None and t2 is not None and "unit.uri" in value_pairs and "concept.uri" in value_pairs:
+        response: [Database] = OpenSearchClient().unit_independent_search(t1, t2, value_pairs, user_id)
     else:
-        response = OpenSearchClient().general_search(field_type, req_body)
+        response: [Database] = OpenSearchClient().general_search(field_type=field_type,
+                                                                 field_value_pairs=value_pairs,
+                                                                 user_id=user_id,
+                                                                 user_token=request.headers.get('Authorization'))
     # filter by type
+    tables = [table for table in flatten([database.tables for database in response]) if
+              table.is_public or table.is_schema_public or (user_id is not None and table.owner.id == user_id)]
+    views = [view for view in flatten([database.views for database in response]) if
+             view.is_public or view.is_schema_public or (user_id is not None and view.owner.id == user_id)]
     if field_type == 'table':
-        tmp = []
-        for database in response:
-            if database["tables"] is not None:
-                for table in database["tables"]:
-                    table["is_public"] = database["is_public"]
-                    tmp.append(table)
-        response = tmp
+        logging.debug(f'filtered to {len(tables)} tables')
+        response = tables
     if field_type == 'identifier':
         tmp = []
         for database in response:
@@ -393,12 +392,7 @@ def post_general_search(field_type):
                 tmp.append(view['identifier'])
         response = tmp
     elif field_type == 'column':
-        response = [x for xs in response for x in xs["tables"]]
-        for table in response:
-            for column in table["columns"]:
-                column["table_id"] = table["id"]
-                column["database_id"] = table["database_id"]
-        response = [x for xs in response for x in xs["columns"]]
+        response = flatten([table.columns for table in tables])
     elif field_type == 'concept':
         tmp = []
         tables = [x for xs in response for x in xs["tables"]]
@@ -414,15 +408,15 @@ def post_general_search(field_type):
                 tmp.append(column["unit"])
         response = tmp
     elif field_type == 'view':
-        response = [x for xs in response for x in xs["views"]]
-    return dict({'results': response, 'type': field_type}), 200
+        response = views
+    return Response(dumps(response, default=pydantic_encoder)), 200, {'Content-Type': 'application/json'}
 
 
 @app.route("/api/search/database/<int:database_id>", methods=["PUT"], endpoint="search_put_database")
 @metrics.gauge(name='dbrepo_search_update_database',
                description='Time needed to update a database in the search database')
 @auth.login_required(role=['update-search-index'])
-def update_database(database_id: int) -> Database | ApiError:
+def update_database(database_id: int):
     logging.debug(f"updating database with id: {database_id}")
     try:
         payload: Database = Database.model_validate(request.json)
@@ -431,7 +425,7 @@ def update_database(database_id: int) -> Database | ApiError:
         return ApiError(status='BAD_REQUEST', message=f'Malformed payload: {e}',
                         code='search.general.missing').model_dump(), 400
     database = OpenSearchClient().update_database(database_id, payload)
-    logging.info(f"Updated database with id : {database_id}")
+    logging.info(f"Updated database with id: {database_id}")
     return database.model_dump(), 202
 
 
@@ -442,7 +436,7 @@ def update_database(database_id: int) -> Database | ApiError:
 def delete_database(database_id: int):
     try:
         OpenSearchClient().delete_database(database_id)
-        return dumps({}), 202
+        return Response(dumps({})), 202
     except NotFoundError:
         return ApiError(status='NOT_FOUND', message='Failed to find database',
                         code='search.database.missing').model_dump(), 404
diff --git a/dbrepo-search-service/init/Pipfile b/dbrepo-search-service/init/Pipfile
index 5c3c4a2ad8767b0f190271524cc5cbfc248a340a..77bab3e84c036d88689f0431bf63dc2f5fe4d099 100644
--- a/dbrepo-search-service/init/Pipfile
+++ b/dbrepo-search-service/init/Pipfile
@@ -9,7 +9,7 @@ opensearch-py = "~=2.2"
 python-dotenv = "~=1.0"
 testcontainers-opensearch = "*"
 pytest = "*"
-dbrepo = {path = "./lib/dbrepo-1.5.1.tar.gz"}
+dbrepo = {path = "./lib/dbrepo-1.6.1.tar.gz"}
 rdflib = "*"
 
 [dev-packages]
diff --git a/dbrepo-search-service/init/Pipfile.lock b/dbrepo-search-service/init/Pipfile.lock
index 4362403bdd15b3976ac8f2c1b10a51bb63ba63ba..bf53ace7e7551b8e0961373d6ab23e49ff53f300 100644
--- a/dbrepo-search-service/init/Pipfile.lock
+++ b/dbrepo-search-service/init/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "0ed594feea5f1f7694d727f04d4bebd9cf341fed8f37e54819289daee0f22eb6"
+            "sha256": "9edba52503b8604b267d52e41954beba012143b1e47f56aaae553cdcaf054e55"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -254,11 +254,10 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:1a52772dedf756bf48c52eec4df0c3a0c1fcef8034f47aa33c7ae5207cf85609",
-                "sha256:a18d7304b3ea3d5ba9c0b94988911ed6a129d59f2116d362ae9e4bf5a2803e2e"
+                "sha256:251f3c2088bbd289cee86d5394b1e62e29aa081f994dd0845d895e3330f6a106"
             ],
-            "markers": "python_version >= '3.11'",
-            "path": "./lib/dbrepo-1.5.1.tar.gz"
+            "path": "./lib/dbrepo-1.6.1.tar.gz",
+            "version": "==1.6.1"
         },
         "docker": {
             "hashes": [
@@ -280,7 +279,6 @@
                 "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==2.3.3"
         },
         "frozenlist": {
@@ -645,7 +643,6 @@
                 "sha256:6598df0bc7a003294edd0ba88a331e0793acbb8c910c43edf398791e3b2eccda"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8' and python_version < '4'",
             "version": "==2.8.0"
         },
         "packaging": {
@@ -810,11 +807,11 @@
         },
         "pydantic": {
             "hashes": [
-                "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d",
-                "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"
+                "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff",
+                "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==2.10.4"
+            "version": "==2.10.5"
         },
         "pydantic-core": {
             "hashes": [
@@ -936,7 +933,6 @@
                 "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==8.3.4"
         },
         "python-dateutil": {
@@ -953,7 +949,6 @@
                 "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==1.0.1"
         },
         "pytz": {
@@ -965,12 +960,11 @@
         },
         "rdflib": {
             "hashes": [
-                "sha256:164de86bd3564558802ca983d84f6616a4a1a420c7a17a8152f5016076b2913e",
-                "sha256:e590fa9a2c34ba33a667818b5a84be3fb8a4d85868f8038f17912ec84f912a25"
+                "sha256:4fc8f6d50b199dc38fbc5256370f038c1cedca6102ccbde4e37c0fd2b7f36e65",
+                "sha256:5a694a64f48a751079999c37dccf91a6210077d845d09adf7c3ce23a876265a7"
             ],
             "index": "pypi",
-            "markers": "python_full_version >= '3.8.1' and python_full_version < '4.0.0'",
-            "version": "==7.1.1"
+            "version": "==7.1.2"
         },
         "requests": {
             "hashes": [
@@ -1000,7 +994,6 @@
                 "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.7'",
             "version": "==0.0.1rc1"
         },
         "tinydb": {
@@ -1053,74 +1046,88 @@
         },
         "wrapt": {
             "hashes": [
-                "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d",
-                "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301",
-                "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635",
-                "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a",
-                "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed",
-                "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721",
-                "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801",
-                "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b",
-                "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1",
-                "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88",
-                "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8",
-                "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0",
-                "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f",
-                "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578",
-                "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7",
-                "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045",
-                "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada",
-                "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d",
-                "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b",
-                "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a",
-                "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977",
-                "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea",
-                "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346",
-                "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13",
-                "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22",
-                "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339",
-                "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9",
-                "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181",
-                "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c",
-                "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90",
-                "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a",
-                "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489",
-                "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f",
-                "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504",
-                "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea",
-                "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569",
-                "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4",
-                "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce",
-                "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab",
-                "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a",
-                "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f",
-                "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c",
-                "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9",
-                "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf",
-                "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d",
-                "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627",
-                "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d",
-                "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4",
-                "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c",
-                "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d",
-                "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad",
-                "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b",
-                "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33",
-                "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371",
-                "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1",
-                "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393",
-                "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106",
-                "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df",
-                "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379",
-                "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451",
-                "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b",
-                "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575",
-                "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed",
-                "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb",
-                "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"
+                "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f",
+                "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c",
+                "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a",
+                "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b",
+                "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555",
+                "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c",
+                "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b",
+                "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6",
+                "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8",
+                "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662",
+                "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061",
+                "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998",
+                "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb",
+                "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62",
+                "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984",
+                "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392",
+                "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2",
+                "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306",
+                "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7",
+                "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3",
+                "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9",
+                "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6",
+                "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192",
+                "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317",
+                "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f",
+                "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda",
+                "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563",
+                "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a",
+                "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f",
+                "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d",
+                "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9",
+                "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8",
+                "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82",
+                "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9",
+                "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845",
+                "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82",
+                "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125",
+                "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504",
+                "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b",
+                "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7",
+                "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc",
+                "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6",
+                "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40",
+                "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a",
+                "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3",
+                "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a",
+                "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72",
+                "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681",
+                "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438",
+                "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae",
+                "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2",
+                "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb",
+                "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5",
+                "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a",
+                "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3",
+                "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8",
+                "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2",
+                "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22",
+                "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72",
+                "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061",
+                "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f",
+                "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9",
+                "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04",
+                "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98",
+                "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9",
+                "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f",
+                "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b",
+                "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925",
+                "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6",
+                "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0",
+                "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9",
+                "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c",
+                "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991",
+                "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6",
+                "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000",
+                "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb",
+                "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119",
+                "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b",
+                "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==1.17.0"
+            "version": "==1.17.2"
         },
         "yarl": {
             "hashes": [
@@ -1278,7 +1285,6 @@
                 "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.9'",
             "version": "==7.6.10"
         },
         "iniconfig": {
@@ -1311,7 +1317,6 @@
                 "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.8'",
             "version": "==8.3.4"
         }
     }
diff --git a/dbrepo-search-service/init/app.py b/dbrepo-search-service/init/app.py
index 9fe915f92c50d2b712058783d4eecf1b087cc8f7..f8f671bade77541508aec72cd19066157cd162f3 100644
--- a/dbrepo-search-service/init/app.py
+++ b/dbrepo-search-service/init/app.py
@@ -1,12 +1,11 @@
 import json
-import os
 import logging
+import os
+from logging.config import dictConfig
 from typing import List
 
 import opensearchpy.exceptions
 from dbrepo.RestClient import RestClient
-from logging.config import dictConfig
-
 from dbrepo.api.dto import Database
 from opensearchpy import OpenSearch
 
@@ -46,6 +45,8 @@ class App:
     search_username: str = None
     search_password: str = None
     search_instance: OpenSearch = None
+    system_username: str = None
+    system_password: str = None
 
     def __init__(self):
         self.metadata_service_endpoint = os.getenv("METADATA_SERVICE_ENDPOINT", "http://metadata-service:8080")
@@ -53,6 +54,8 @@ class App:
         self.search_port = int(os.getenv("OPENSEARCH_PORT", "9200"))
         self.search_username = os.getenv("OPENSEARCH_USERNAME", "admin")
         self.search_password = os.getenv("OPENSEARCH_PASSWORD", "admin")
+        self.system_username = os.getenv("SYSTEM_USERNAME", "admin")
+        self.system_password = os.getenv("SYSTEM_PASSWORD", "admin")
 
     def _instance(self) -> OpenSearch:
         """
@@ -84,7 +87,8 @@ class App:
 
     def fetch_databases(self) -> List[Database]:
         logging.debug(f"fetching database from endpoint: {self.metadata_service_endpoint}")
-        client = RestClient(endpoint=self.metadata_service_endpoint)
+        client = RestClient(endpoint=self.metadata_service_endpoint, username=self.system_username,
+                            password=self.system_password)
         databases = []
         for index, database in enumerate(client.get_databases()):
             logging.debug(f"fetching database {index}/{len(databases)} details for database id: {database.id}")
@@ -93,16 +97,17 @@ class App:
         return databases
 
     def save_databases(self, databases: List[Database]):
-        logging.debug(f"save {len(databases)} database(s)")
+        index = f'database'
+        logging.debug(f"save {len(databases)} database(s) in index: {index}")
         for doc in databases:
             doc: Database = doc
             try:
-                self._instance().delete(index="database", id=doc.id)
-                logging.debug(f"deleted database with id {doc.id}")
+                self._instance().delete(index=index, id=doc.id)
+                logging.debug(f"truncated database with id {doc.id} in index: {index}")
             except opensearchpy.NotFoundError:
-                logging.warning(f"Database with id {doc.id} does not exist, skip.")
-            self._instance().create(index="database", id=doc.id, body=doc.model_dump())
-            logging.debug(f"created database with id {doc.id}")
+                pass
+            self._instance().create(index=index, id=doc.id, body=doc.model_dump())
+            logging.info(f"Saved database with id {doc.id} in index: {index}")
 
 
 if __name__ == "__main__":
diff --git a/dbrepo-search-service/init/clients/keycloak_client.py b/dbrepo-search-service/init/clients/keycloak_client.py
index afa36a1112ce41b5686641f5691df3f44075cf2f..2e15d00a9b272233feb7bab4cf6166c63b151e04 100644
--- a/dbrepo-search-service/init/clients/keycloak_client.py
+++ b/dbrepo-search-service/init/clients/keycloak_client.py
@@ -1,14 +1,17 @@
 import logging
 from dataclasses import dataclass
-import requests
-from flask import current_app
 from typing import List
 
+import requests
+from dbrepo.api.dto import ApiError
+from flask import current_app
 from jwt import jwk_from_pem, JWT
+from jwt.exceptions import JWTDecodeError
 
 
 @dataclass(init=True, eq=True)
 class User:
+    id: str
     username: str
     roles: List[str]
 
@@ -30,8 +33,22 @@ class KeycloakClient:
             raise AssertionError("Failed to obtain user token(s)")
         return response.json()["access_token"]
 
-    def verify_jwt(self, access_token: str) -> User:
+    def verify_jwt(self, access_token: str) -> ApiError | User:
         public_key = jwk_from_pem(str(current_app.config["JWT_PUBKEY"]).encode('utf-8'))
         payload = JWT().decode(message=access_token, key=public_key, do_time_check=True)
-        logging.debug(f"JWT token client_id={payload.get('client_id')} and realm_access={payload.get('realm_access')}")
-        return User(username=payload.get('client_id'), roles=payload.get('realm_access')["roles"])
+        return User(id=payload.get('uid'), username=payload.get('client_id'),
+                    roles=payload.get('realm_access')["roles"])
+
+    def userId(self, auth_header: str | None) -> (str | None, ApiError, int):
+        if auth_header is None:
+            return None, None, None
+        try:
+            user = self.verify_jwt(auth_header.split(" ")[1])
+            logging.debug(f'mapped JWT to user.id {user.id}')
+            return user.id, None, None
+        except JWTDecodeError as e:
+            logging.error(f'Failed to decode JWT: {e}')
+            if str(e) == 'JWT Expired':
+                return None, ApiError(status='UNAUTHORIZED', message=f'Token expired',
+                                      code='search.user.unauthorized').model_dump(), 401
+            return None, ApiError(status='FORBIDDEN', message=str(e), code='search.user.forbidden').model_dump(), 403
diff --git a/dbrepo-search-service/init/clients/opensearch_client.py b/dbrepo-search-service/init/clients/opensearch_client.py
index 7d25fcded5a29e87524523785133d7aaa56f314d..35c26f03f5f684adc2652c199db7f99afd6cfc13 100644
--- a/dbrepo-search-service/init/clients/opensearch_client.py
+++ b/dbrepo-search-service/init/clients/opensearch_client.py
@@ -1,17 +1,18 @@
 """
 The opensearch_client.py is used by the different API endpoints in routes.py to handle requests  to the opensearch db
 """
+import logging
 import os
+from collections.abc import MutableMapping
 from json import dumps, load
-import logging
 
 from dbrepo.api.dto import Database
-from collections.abc import MutableMapping
-
-from opensearchpy import OpenSearch, TransportError, RequestError, NotFoundError
+from dbrepo.api.exceptions import ForbiddenError, NotExistsError
+from opensearchpy import OpenSearch, NotFoundError
+from requests import head
 
-from omlib.measure import om
 from omlib.constants import OM_IDS
+from omlib.measure import om
 from omlib.omconstants import OM
 from omlib.unit import Unit
 
@@ -21,16 +22,22 @@ class OpenSearchClient:
     The client to communicate with the OpenSearch database.
     """
     host: str = None
+    instance: OpenSearch = None
+    metadata_endpoint: str = None
+    password: str = None
     port: int = None
+    system_username: str = None
+    system_password: str = None
     username: str = None
-    password: str = None
-    instance: OpenSearch = None
 
     def __init__(self, host: str = None, port: int = None, username: str = None, password: str = None):
         self.host = os.getenv('OPENSEARCH_HOST', host)
+        self.metadata_endpoint = os.getenv('METADATA_SERVICE_ENDPOINT', 'http://metadata-service:8080')
+        self.password = os.getenv('OPENSEARCH_PASSWORD', password)
         self.port = int(os.getenv('OPENSEARCH_PORT', port))
+        self.system_username = os.getenv('SYSTEM_USERNAME', 'admin')
+        self.system_password = os.getenv('SYSTEM_PASSWORD', 'admin')
         self.username = os.getenv('OPENSEARCH_USERNAME', username)
-        self.password = os.getenv('OPENSEARCH_PASSWORD', password)
 
     def _instance(self) -> OpenSearch:
         """
@@ -44,18 +51,6 @@ class OpenSearchClient:
                                        http_auth=(self.username, self.password))
         return self.instance
 
-    def get_database(self, database_id: int) -> Database:
-        """
-        Gets a database by given id.
-
-        @param database_id: The database id.
-
-        @returns: The database, if successful.
-        @throws: opensearchpy.exceptions.NotFoundError If the database was not found in the Search Database.
-        """
-        response: dict = self._instance().get(index="database", id=database_id)
-        return Database.parse_obj(response["_source"])
-
     def update_database(self, database_id: int, data: Database) -> Database:
         """
         Updates the database data with given id.
@@ -69,9 +64,7 @@ class OpenSearchClient:
         logging.debug(f"updating database with id: {database_id} in search database")
         self._instance().index(index="database", id=database_id, body=dumps(data.model_dump()))
         response: dict = self._instance().get(index="database", id=database_id)
-        database = Database.parse_obj(response["_source"])
-        logging.info(f"Updated database with id {database_id} in index 'database'")
-        return database
+        return Database.model_validate(response["_source"])
 
     def delete_database(self, database_id: int) -> None:
         """
@@ -124,7 +117,7 @@ class OpenSearchClient:
             "unit": "tables.columns.unit.*",
             "identifier": "identifiers.*",
             "view": "views.*",
-            "user": "creator.*",
+            "user": "owner.*",
         }
         if field_type not in fields.keys():
             raise NotFoundError(f"Failed to find field type: {field_type}")
@@ -143,27 +136,50 @@ class OpenSearchClient:
                 fields_list.append(entry)
         return fields_list
 
-    def fuzzy_search(self, search_term=None):
-        logging.info(f"Performing fuzzy search")
-        fuzzy_body = {
-            "query": {
-                "multi_match": {
-                    "query": search_term,
-                    "fuzziness": "AUTO",
-                    "fuzzy_transpositions": True,
-                    "minimum_should_match": 3
-                }
-            }
-        }
-        logging.debug(f'search body: {fuzzy_body}')
+    def fuzzy_search(self, search_term: str, user_id: str | None = None, user_token: str | None = None) -> [Database]:
         response = self._instance().search(
             index="database",
-            body=fuzzy_body
+            body={
+                "query": {
+                    "multi_match": {
+                        "query": search_term,
+                        "fuzziness": "AUTO",
+                        "prefix_length": 2
+                    }
+                }
+            }
         )
-        logging.info(f"Found {len(response['hits']['hits'])} result(s)")
-        return response
-
-    def general_search(self, field_type: str = None, field_value_pairs: dict = None):
+        results: [Database] = []
+        if "hits" in response and "hits" in response["hits"]:
+            results = [Database.model_validate(hit["_source"]) for hit in response["hits"]["hits"]]
+        logging.debug(f'found {len(results)} results')
+        return self.filter_results(results, user_id, user_token)
+
+    def filter_results(self, results: [Database], user_id: str | None = None, user_token: str | None = None) -> [
+        Database]:
+        filtered: [Database] = []
+        for database in results:
+            if database.is_public or database.is_schema_public:
+                logging.debug(f'database with id {database.id} is public or has public schema')
+                filtered.append(database)
+            elif user_id is not None and user_token is not None:
+                try:
+                    url = f'{self.metadata_endpoint}/api/database/{database.id}/access/{user_id}'
+                    logging.debug(f'requesting access from url: {url}')
+                    response = head(url=url, auth=(self.system_username, self.system_password))
+                    if response.status_code == 200:
+                        logging.debug(f'database with id {database.id} is draft and access was found')
+                        filtered.append(database)
+                    else:
+                        logging.warning(
+                            f'database with id {database.id} is not accessible: code {response.status_code}')
+                except (ForbiddenError, NotExistsError) as e:
+                    logging.warning(f'database with id {database.id} is draft but no access was found')
+        logging.debug(f'filtered {len(filtered)} results')
+        return filtered
+
+    def general_search(self, field_type: str = None, field_value_pairs: dict = None, user_id: str | None = None,
+                       user_token: str | None = None) -> [Database]:
         """
         Main method for searching stuff in the opensearch db
 
@@ -204,10 +220,14 @@ class OpenSearchClient:
             index="database",
             body=dumps(body)
         )
-        results = [hit["_source"] for hit in response["hits"]["hits"]]
-        return results
-
-    def unit_independent_search(self, t1: float, t2: float, field_value_pairs):
+        results: [Database] = []
+        if "hits" in response and "hits" in response["hits"]:
+            results = [Database.model_validate(hit["_source"]) for hit in response["hits"]["hits"]]
+        logging.debug(f'found {len(results)} results')
+        return self.filter_results(results, user_id, user_token)
+
+    def unit_independent_search(self, t1: float, t2: float, field_value_pairs: dict, userId: str | None = None) -> [
+        Database]:
         """
         Main method for searching stuff in the opensearch db
 
@@ -288,16 +308,12 @@ class OpenSearchClient:
         body = ''
         for search in searches:
             body += '%s \n' % dumps(search)
-        responses = self._instance().msearch(
+        response = self._instance().msearch(
             body=dumps(body)
         )
-        response = {
-            "hits": {
-                "hits": flatten([hits["hits"]["hits"] for hits in responses["responses"]])
-            },
-            "took": responses["took"]
-        }
-        return response
+        results = flatten([hits["hits"]["hits"] for hits in response["responses"]])
+        return [database for database in results if
+                database.is_public or database.is_schema_public or (userId is not None and database.owner.id == userId)]
 
 
 def key_to_attr_name(key: str) -> str:
@@ -324,7 +340,7 @@ def attr_name_to_attr_friendly_name(key: str) -> str:
     :param key: The attribute key
     :return: The human-readable representation of the attribute key
     """
-    with open('friendly_names_overrides.json') as json_data:
+    with open('./friendly_names_overrides.json') as json_data:
         d = load(json_data)
         for json_key in d.keys():
             if json_key == key:
diff --git a/dbrepo-search-service/init/database.json b/dbrepo-search-service/init/database.json
index fb175700c614599daab44fa0fb77e51a05151b62..59cbd78438a5fff391d1237c70a0384d7b397a83 100644
--- a/dbrepo-search-service/init/database.json
+++ b/dbrepo-search-service/init/database.json
@@ -244,7 +244,7 @@
       "created": {
         "type": "date"
       },
-      "creator": {
+      "owner": {
         "properties": {
           "id": {
             "type": "text",
@@ -310,7 +310,7 @@
           "created": {
             "type": "date"
           },
-          "creator": {
+          "owner": {
             "properties": {
               "id": {
                 "type": "text",
@@ -359,9 +359,9 @@
               }
             }
           },
-          "creators": {
+          "owners": {
             "properties": {
-              "creator_name": {
+              "owner_name": {
                 "type": "text",
                 "fields": {
                   "keyword": {
@@ -572,37 +572,6 @@
           }
         }
       },
-      "owner": {
-        "properties": {
-          "id": {
-            "type": "text",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          },
-          "qualified_name": {
-            "type": "text",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          },
-          "username": {
-            "type": "text",
-            "fields": {
-              "keyword": {
-                "type": "keyword",
-                "ignore_above": 256
-              }
-            }
-          }
-        }
-      },
       "tables": {
         "properties": {
           "columns": {
@@ -610,7 +579,7 @@
               "auto_generated": {
                 "type": "boolean"
               },
-              "column_type": {
+              "type": {
                 "type": "text",
                 "fields": {
                   "keyword": {
@@ -643,6 +612,9 @@
               "is_public": {
                 "type": "boolean"
               },
+              "is_schema_public": {
+                "type": "boolean"
+              },
               "mean": {
                 "type": "float"
               },
@@ -733,7 +705,7 @@
           "created": {
             "type": "date"
           },
-          "creator": {
+          "owner": {
             "properties": {
               "id": {
                 "type": "text",
@@ -830,55 +802,6 @@
           "num_rows": {
             "type": "long"
           },
-          "owner": {
-            "properties": {
-              "id": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "qualified_name": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "name": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "orcid": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              },
-              "username": {
-                "type": "text",
-                "fields": {
-                  "keyword": {
-                    "type": "keyword",
-                    "ignore_above": 256
-                  }
-                }
-              }
-            }
-          },
           "queue_name": {
             "type": "text",
             "fields": {
@@ -906,7 +829,7 @@
               "auto_generated": {
                 "type": "boolean"
               },
-              "column_type": {
+              "type": {
                 "type": "text",
                 "fields": {
                   "keyword": {
@@ -936,6 +859,9 @@
               "is_public": {
                 "type": "boolean"
               },
+              "is_schema_public": {
+                "type": "boolean"
+              },
               "name": {
                 "type": "text",
                 "fields": {
@@ -950,7 +876,7 @@
           "created": {
             "type": "date"
           },
-          "creator": {
+          "owner": {
             "properties": {
               "id": {
                 "type": "text",
@@ -1010,7 +936,7 @@
               "created": {
                 "type": "date"
               },
-              "creator": {
+              "owner": {
                 "properties": {
                   "id": {
                     "type": "text",
@@ -1059,9 +985,9 @@
                   }
                 }
               },
-              "creators": {
+              "owners": {
                 "properties": {
-                  "creator_name": {
+                  "owner_name": {
                     "type": "text",
                     "fields": {
                       "keyword": {
diff --git a/dbrepo-search-service/init/lib/dbrepo-1.5.1.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.5.1.tar.gz
deleted file mode 100644
index ca01f15711ae3ba792d5ecfca40356b50dda6b15..0000000000000000000000000000000000000000
Binary files a/dbrepo-search-service/init/lib/dbrepo-1.5.1.tar.gz and /dev/null differ
diff --git a/dbrepo-search-service/init/lib/dbrepo-1.5.2.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.5.2.tar.gz
deleted file mode 100644
index 52bf0fe9a030ce509450bc52427eb7e2c1f1da13..0000000000000000000000000000000000000000
Binary files a/dbrepo-search-service/init/lib/dbrepo-1.5.2.tar.gz and /dev/null differ
diff --git a/dbrepo-search-service/init/lib/dbrepo-1.5.3.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.5.3.tar.gz
deleted file mode 100644
index 2bb796d8fece2d97c3b2168248ff493dfa24a549..0000000000000000000000000000000000000000
Binary files a/dbrepo-search-service/init/lib/dbrepo-1.5.3.tar.gz and /dev/null differ
diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..7914db1bb84dddf85611cda3b766c0c0cdc094c7
Binary files /dev/null and b/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz differ
diff --git a/dbrepo-search-service/lib/dbrepo-1.5.1.tar.gz b/dbrepo-search-service/lib/dbrepo-1.5.1.tar.gz
deleted file mode 100644
index ca01f15711ae3ba792d5ecfca40356b50dda6b15..0000000000000000000000000000000000000000
Binary files a/dbrepo-search-service/lib/dbrepo-1.5.1.tar.gz and /dev/null differ
diff --git a/dbrepo-search-service/lib/dbrepo-1.5.2.tar.gz b/dbrepo-search-service/lib/dbrepo-1.5.2.tar.gz
deleted file mode 100644
index 52bf0fe9a030ce509450bc52427eb7e2c1f1da13..0000000000000000000000000000000000000000
Binary files a/dbrepo-search-service/lib/dbrepo-1.5.2.tar.gz and /dev/null differ
diff --git a/dbrepo-search-service/lib/dbrepo-1.5.3.tar.gz b/dbrepo-search-service/lib/dbrepo-1.5.3.tar.gz
deleted file mode 100644
index 2bb796d8fece2d97c3b2168248ff493dfa24a549..0000000000000000000000000000000000000000
Binary files a/dbrepo-search-service/lib/dbrepo-1.5.3.tar.gz and /dev/null differ
diff --git a/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz b/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..7914db1bb84dddf85611cda3b766c0c0cdc094c7
Binary files /dev/null and b/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz differ
diff --git a/dbrepo-search-service/os-yml/get_fuzzy_search.yml b/dbrepo-search-service/os-yml/get_fuzzy_search.yml
index bc54419eb9735fe731fb12a5e911070ebc29f80e..db2ef87b3268f10329ba11117ee323208d940af5 100644
--- a/dbrepo-search-service/os-yml/get_fuzzy_search.yml
+++ b/dbrepo-search-service/os-yml/get_fuzzy_search.yml
@@ -19,6 +19,9 @@ responses:
     content:
       application/json:
         schema:
-          $ref: '#/components/schemas/SearchResultDto'
+          type: array
+          properties:
+            id:
+              type: string
   415:
     description: Wrong accept type
diff --git a/dbrepo-search-service/test/test_app.py b/dbrepo-search-service/test/test_app.py
index 0482ff3f586050f49135039bb36ec5d16320396d..0c577b228a951064ffd650b860100ad17f80ba72 100644
--- a/dbrepo-search-service/test/test_app.py
+++ b/dbrepo-search-service/test/test_app.py
@@ -3,7 +3,7 @@ import time
 import unittest
 
 import jwt
-from dbrepo.api.dto import Database, Table, Constraints, Column, ColumnType, Concept, Unit, \
+from dbrepo.api.dto import Database, Table, Constraints, Column, ColumnType, ConceptBrief, UnitBrief, \
     UserBrief, ContainerBrief, ImageBrief
 
 from app import app
@@ -32,12 +32,21 @@ req = Database(id=1,
                              routing_key="dbrepo.1.1",
                              is_public=True,
                              is_schema_public=True,
-                             columns=[Column(id=1, database_id=1, table_id=1, name="ID", internal_name="id",
-                                             column_type=ColumnType.BIGINT, is_public=True, is_null_allowed=False,
-                                             size=20, d=0,
-                                             concept=Concept(id=1, uri="http://www.wikidata.org/entity/Q2221906"),
-                                             unit=Unit(id=1,
-                                                       uri="http://www.ontology-of-units-of-measure.org/resource/om-2/degreeCelsius"),
+                             columns=[Column(id=1,
+                                             database_id=1,
+                                             table_id=1,
+                                             ord=0,
+                                             name="ID",
+                                             internal_name="id",
+                                             type=ColumnType.BIGINT,
+                                             is_public=True,
+                                             is_null_allowed=False,
+                                             size=20,
+                                             d=0,
+                                             concept=ConceptBrief(id=1,
+                                                                  uri="http://www.wikidata.org/entity/Q2221906"),
+                                             unit=UnitBrief(id=1,
+                                                            uri="http://www.ontology-of-units-of-measure.org/resource/om-2/degreeCelsius"),
                                              val_min=0,
                                              val_max=10)]
                              )])
diff --git a/dbrepo-search-service/test/test_jwt.py b/dbrepo-search-service/test/test_jwt.py
index 59cd4ee1168117d0aeb6bf3549fe5088edc379b9..96ce8410da4be0598fd6d635e7decc9950bd42e8 100644
--- a/dbrepo-search-service/test/test_jwt.py
+++ b/dbrepo-search-service/test/test_jwt.py
@@ -12,9 +12,9 @@ class JwtTest(unittest.TestCase):
 
     def response(self, roles: [str]) -> dict:
         return dict({
-            "client_id": "username",
-            "realm_access": {
-                "roles": roles
+            'client_id': 'username',
+            'realm_access': {
+                'roles': roles
             }
         })
 
@@ -37,13 +37,13 @@ class JwtTest(unittest.TestCase):
     def test_verify_token_empty_token_fails(self):
         with app.app_context():
             # test
-            user = verify_token("")
+            user = verify_token('')
             self.assertFalse(user)
 
     def test_verify_token_malformed_token_fails(self):
         with app.app_context():
             # test
-            user = verify_token("eyEYEY12345")
+            user = verify_token('eyEYEY12345')
             self.assertFalse(user)
 
     def test_verify_token_succeeds(self):
@@ -59,25 +59,25 @@ class JwtTest(unittest.TestCase):
     def test_verify_password_no_username_fails(self):
         with app.app_context():
             # test
-            user = verify_password(None, "pass")
+            user = verify_password(None, 'pass')
             self.assertFalse(user)
 
     def test_verify_password_empty_username_fails(self):
         with app.app_context():
             # test
-            user = verify_password("", "pass")
+            user = verify_password('', 'pass')
             self.assertFalse(user)
 
     def test_verify_password_no_password_fails(self):
         with app.app_context():
             # test
-            user = verify_password("username", None)
+            user = verify_password('username', None)
             self.assertFalse(user)
 
     def test_verify_password_empty_password_fails(self):
         with app.app_context():
             # test
-            user = verify_password("username", "")
+            user = verify_password('username', '')
             self.assertFalse(user)
 
     def test_verify_password_succeeds(self):
@@ -87,11 +87,12 @@ class JwtTest(unittest.TestCase):
                 mock.post('http://auth-service:8080/api/auth/realms/dbrepo/protocol/openid-connect/token',
                           json=self.response([]))
                 # test
-                user = verify_password("username", "password")
+                user = verify_password('username', 'password')
                 self.assertIsNotNone(user)
 
     def test_get_user_roles_succeeds(self):
         with app.app_context():
             # test
-            roles: [str] = get_user_roles(User(username="username", roles=[]))
+            roles: [str] = get_user_roles(
+                User(id='b98415d8-28bc-4472-84ff-3d09cc79aff6', username='username', roles=[]))
             self.assertEqual([], roles)
diff --git a/dbrepo-search-service/test/test_opensearch_client.py b/dbrepo-search-service/test/test_opensearch_client.py
index 295b2917af7995f56ef0d962fac0948334c9aeff..9da77adfde53e155dddc36f364bea9d974964125 100644
--- a/dbrepo-search-service/test/test_opensearch_client.py
+++ b/dbrepo-search-service/test/test_opensearch_client.py
@@ -2,7 +2,7 @@ import unittest
 
 import opensearchpy
 from dbrepo.api.dto import Database, Table, Column, ColumnType, Constraints, PrimaryKey, \
-    TableMinimal, ColumnMinimal, Concept, Unit, UserBrief, ContainerBrief, ImageBrief
+    TableMinimal, ColumnMinimal, ConceptBrief, UnitBrief, UserBrief, ContainerBrief, ImageBrief
 from opensearchpy import NotFoundError
 
 from app import app
@@ -35,12 +35,19 @@ req = Database(id=1,
                              routing_key="dbrepo.1.1",
                              is_public=True,
                              is_schema_public=True,
-                             columns=[Column(id=1, database_id=1, table_id=1, name="ID", internal_name="id",
-                                             column_type=ColumnType.BIGINT, is_public=True, is_null_allowed=False,
-                                             size=20, d=0,
-                                             concept=Concept(id=1, uri="http://www.wikidata.org/entity/Q2221906"),
-                                             unit=Unit(id=1,
-                                                       uri="http://www.ontology-of-units-of-measure.org/resource/om-2/degreeCelsius"),
+                             columns=[Column(id=1,
+                                             database_id=1,
+                                             table_id=1,
+                                             name="ID",
+                                             ord=0,
+                                             internal_name="id",
+                                             type=ColumnType.BIGINT,
+                                             is_null_allowed=False,
+                                             size=20,
+                                             d=0,
+                                             concept=ConceptBrief(id=1, uri="http://www.wikidata.org/entity/Q2221906"),
+                                             unit=UnitBrief(id=1,
+                                                            uri="http://www.ontology-of-units-of-measure.org/resource/om-2/degreeCelsius"),
                                              val_min=0,
                                              val_max=10)]
                              )])
@@ -71,12 +78,12 @@ class OpenSearchClientTest(unittest.TestCase):
                                 is_versioned=True,
                                 owner=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
                                 columns=[Column(id=1,
-                                                name="ID",
-                                                internal_name="id",
                                                 database_id=req.id,
                                                 table_id=1,
-                                                column_type=ColumnType.BIGINT,
-                                                is_public=True,
+                                                ord=0,
+                                                name="ID",
+                                                internal_name="id",
+                                                type=ColumnType.BIGINT,
                                                 is_null_allowed=False)])]
             database = OpenSearchClient().update_database(database_id=req.id, data=req)
             self.assertEqual(1, database.id)
@@ -107,10 +114,9 @@ class OpenSearchClientTest(unittest.TestCase):
             self.assertEqual(1, database.tables[0].columns[0].id)
             self.assertEqual("ID", database.tables[0].columns[0].name)
             self.assertEqual("id", database.tables[0].columns[0].internal_name)
-            self.assertEqual(ColumnType.BIGINT, database.tables[0].columns[0].column_type)
+            self.assertEqual(ColumnType.BIGINT, database.tables[0].columns[0].type)
             self.assertEqual(1, database.tables[0].columns[0].database_id)
             self.assertEqual(1, database.tables[0].columns[0].table_id)
-            self.assertEqual(True, database.tables[0].columns[0].is_public)
             self.assertEqual(False, database.tables[0].columns[0].is_null_allowed)
 
     def test_update_database_create_succeeds(self):
@@ -159,27 +165,6 @@ class OpenSearchClientTest(unittest.TestCase):
             # test
             OpenSearchClient().delete_database(database_id=req.id)
 
-    def test_get_database_succeeds(self):
-        with app.app_context():
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            database = OpenSearchClient().get_database(database_id=req.id)
-            self.assertEqual(req.id, database.id)
-
-    def test_get_database_fails(self):
-        with app.app_context():
-
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            try:
-                OpenSearchClient().get_database(database_id=req.id)
-            except opensearchpy.exceptions.NotFoundError:
-                pass
-
     def test_get_fields_for_index_database_succeeds(self):
         with app.app_context():
             # mock
@@ -204,8 +189,7 @@ class OpenSearchClientTest(unittest.TestCase):
             OpenSearchClient().update_database(database_id=req.id, data=req)
 
             # test
-            response = OpenSearchClient().fuzzy_search(search_term="test")
-            self.assertTrue(len(response) > 0)
+            OpenSearchClient().fuzzy_search(search_term="test_tuw")
 
     def test_unit_independent_search_fails(self):
         with app.app_context():
diff --git a/dbrepo-storage-service/init/Dockerfile b/dbrepo-storage-service/init/Dockerfile
index 4f4f0c797434901d27311cadbbe250bb0e96c480..be95322ff4721d63a62dc4cf8e18c0f768db8eaa 100644
--- a/dbrepo-storage-service/init/Dockerfile
+++ b/dbrepo-storage-service/init/Dockerfile
@@ -8,6 +8,6 @@ USER 1001
 
 WORKDIR /app
 
-COPY --chown=1001 --chmod=0744 ./init.sh /app/init.sh
+COPY --chown=1001 --chmod=0777 ./init.sh /app/init.sh
 
 ENTRYPOINT [ "bash", "-c", "/app/init.sh" ]
diff --git a/dbrepo-ui/bun.lockb b/dbrepo-ui/bun.lockb
index b82e45d505fdc0e1f41e246f7d1caefe3e23007c..45a8c51c621dab127458d68a4493b23d2df9f88d 100755
Binary files a/dbrepo-ui/bun.lockb and b/dbrepo-ui/bun.lockb differ
diff --git a/dbrepo-ui/components/JumboBox.vue b/dbrepo-ui/components/JumboBox.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d2b804f819f22782ba4895c601c8b2ea118ce4e3
--- /dev/null
+++ b/dbrepo-ui/components/JumboBox.vue
@@ -0,0 +1,59 @@
+<template>
+  <div>
+    <v-row>
+      <v-col>
+        <v-card
+          elevation="0"
+          variant="flat"
+          class="mx-auto"
+          max-width="600"
+          :title="title"
+          :subtitle="subtitle"
+          :text="text">
+          <template v-slot:prepend>
+            <v-icon
+              :color="iconColor">
+              {{ icon }}
+            </v-icon>
+          </template>
+        </v-card>
+      </v-col>
+    </v-row>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    title: {
+      type: String,
+      default: () => {
+        return 'Title'
+      }
+    },
+    subtitle: {
+      type: String,
+      default: () => {
+        return 'Subtitle'
+      }
+    },
+    text: {
+      type: String,
+      default: () => {
+        return 'Text'
+      }
+    },
+    icon: {
+      type: String,
+      default: () => {
+        return 'mdi-alert-rhombus-outline'
+      }
+    },
+    iconColor: {
+      type: String,
+      default: () => {
+        return 'warning'
+      }
+    }
+  }
+}
+</script>
diff --git a/dbrepo-ui/components/ResourceStatus.vue b/dbrepo-ui/components/ResourceStatus.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5167d899ea6e73c150db478f8fc3b063a3ba1e12
--- /dev/null
+++ b/dbrepo-ui/components/ResourceStatus.vue
@@ -0,0 +1,69 @@
+<template>
+  <span
+    v-if="mode">
+    <v-chip
+      v-if="!inline"
+      :size="size"
+      :color="color"
+      variant="outlined">
+      {{ status }}
+    </v-chip>
+    <span
+      v-else>
+      {{ status }}
+    </span>
+  </span>
+</template>
+<script>
+export default {
+  props: {
+    resource: {
+      default: () => {
+        return null
+      }
+    },
+    inline: {
+      default: () => {
+        return false
+      }
+    },
+    size: {
+      default: () => {
+        return 'small'
+      }
+    }
+  },
+  computed: {
+    mode () {
+      if (!this.resource) {
+        return null
+      }
+      if (!this.resource.is_public && !this.resource.is_schema_public) {
+        return 'draft'
+      } else if(!this.resource.is_public && this.resource.is_schema_public) {
+        return 'schema'
+      } else if(this.resource.is_public && !this.resource.is_schema_public) {
+        return 'data'
+      }
+      return 'public'
+    },
+    status () {
+      if (!this.resource) {
+        return null
+      }
+      return this.$t(`pages.database.status.${this.mode}`)
+    },
+    color () {
+      switch (this.mode) {
+        case 'schema':
+        case 'data':
+          return 'warning'
+        case 'draft':
+          return 'error'
+        case 'public':
+          return 'success'
+      }
+    }
+  }
+}
+</script>
diff --git a/dbrepo-ui/components/database/DatabaseCard.vue b/dbrepo-ui/components/database/DatabaseCard.vue
index fba3853a31cf25649f915d4c0a3e422f5ec879bf..c31d288c6fde7ed711ab8d1cab5e5da22a38876e 100644
--- a/dbrepo-ui/components/database/DatabaseCard.vue
+++ b/dbrepo-ui/components/database/DatabaseCard.vue
@@ -27,21 +27,8 @@
         {{ identifierDescription(database) }}
       </div>
       <div class="mt-2 db-tags">
-        <v-chip
-          v-if="database.is_public"
-          size="small"
-          color="success"
-          variant="outlined">
-          {{ $t('toolbars.database.public') }}
-        </v-chip>
-        <v-chip
-          v-if="!database.is_public"
-          size="small"
-          :color="colorVariant"
-          variant="outlined"
-          flat>
-          {{ $t('toolbars.database.private') }}
-        </v-chip>
+        <ResourceStatus
+          :resource="database" />
         <v-chip
           v-if="identifierYear(database)"
           size="small"
@@ -86,8 +73,12 @@
 
 <script>
 import { formatLanguage } from '@/utils'
+import ResourceStatus from '@/components/ResourceStatus.vue'
 
 export default {
+  components: {
+    ResourceStatus
+  },
   data() {
     return {
       loading: false
@@ -175,7 +166,7 @@ export default {
         return null
       }
       return this.identifiers[0]
-    },
+    }
   }
 }
 </script>
diff --git a/dbrepo-ui/components/database/DatabaseCreate.vue b/dbrepo-ui/components/database/DatabaseCreate.vue
index d0f386b08899b2c3f911958297e88d7d67bba4f8..07fd9d34ea218a91bb6ffb634d210bd5493ad830 100644
--- a/dbrepo-ui/components/database/DatabaseCreate.vue
+++ b/dbrepo-ui/components/database/DatabaseCreate.vue
@@ -17,7 +17,7 @@
           <v-row dense>
             <v-col>
               <v-text-field
-                v-model="payload.name"
+                v-model="name"
                 name="database"
                 :variant="inputVariant"
                 :label="$t('pages.database.subpages.create.name.label')"
@@ -55,33 +55,12 @@
           </v-row>
           <v-row>
             <v-col>
-              <v-select
-                v-model="mode"
-                name="mode"
-                :label="$t('pages.database.subpages.create.visibility.label')"
-                :hint="$t('pages.database.subpages.create.visibility.hint')"
-                persistent-hint
-                :variant="inputVariant"
-                :items="visibilityOptions"
-                item-title="name"
-                item-value="value"
-                :rules="[v => !!v || $t('validation.required')]"
-                return-object
-                required>
-                <template
-                  v-slot:append-inner>
-                  <v-tooltip
-                    location="bottom">
-                    <template
-                      v-slot:activator="{ props }">
-                      <v-icon
-                        v-bind="props"
-                        icon="mdi-help-circle-outline" />
-                    </template>
-                    {{ mode.hint }}
-                  </v-tooltip>
-                </template>
-              </v-select>
+              <v-checkbox
+                v-model="draft"
+                name="draft"
+                :label="$t('pages.database.subpages.create.draft.label')"
+                :hint="$t('pages.database.subpages.create.draft.hint')"
+                persistent-hint />
             </v-col>
           </v-row>
         </v-card-text>
@@ -117,6 +96,8 @@ export default {
       loading: false,
       loadingContainers: false,
       engine: null,
+      draft: true,
+      name: null,
       engines: [],
       visibilityOptions: [
         {
@@ -129,13 +110,7 @@ export default {
           hint: this.$t('pages.database.subpages.create.visibility.private.hint'),
           value: false
         }
-      ],
-      mode: true,
-      payload: {
-        name: null,
-        is_public: true,
-        is_schema_public: true,
-      }
+      ]
     }
   },
   computed: {
@@ -179,16 +154,16 @@ export default {
         .catch(({code}) => {
           this.loadingContainers = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
         })
     },
     create () {
       this.loading = true
-      this.payload.container_id = this.engine.id
-      this.payload.is_public = this.mode.value
-      this.payload.is_schema_public = this.mode.value
       const databaseService = useDatabaseService()
-      databaseService.create(this.payload)
+      databaseService.create({ name: this.name, container_id: this.engine.id, is_public: !this.draft, is_schema_public: !this.draft })
         .then(async (database) => {
           await this.$router.push(`/database/${database.id}/info`)
           this.loading = false
diff --git a/dbrepo-ui/components/database/DatabaseToolbar.vue b/dbrepo-ui/components/database/DatabaseToolbar.vue
index 65363c3467067aa4dd3eca9e2d5595a913723684..59b0bc6db65fe888ba74df900580193ff16e92f2 100644
--- a/dbrepo-ui/components/database/DatabaseToolbar.vue
+++ b/dbrepo-ui/components/database/DatabaseToolbar.vue
@@ -8,62 +8,51 @@
           type="subtitle"
           width="200" />
         <span
-          v-if="database && $vuetify.display.lgAndUp">
+          class="mr-2"
+          v-if="database && $vuetify.display.mdAndUp">
           {{ database.name }}
         </span>
-        <v-chip
-          v-if="database && database.is_public"
-          size="small"
-          class="ml-2"
-          color="success"
-          :text="$t('toolbars.database.public')"
-          variant="outlined" />
-        <v-chip
-          v-if="database && !database.is_public"
-          size="small"
-          class="ml-2"
-          :color="colorVariant"
-          variant="outlined"
-          :text="$t('toolbars.database.private')"
-          flat />
+        <ResourceStatus
+          :size="$vuetify.display.mdAndUp ? 'small' : 'default'"
+          :resource="database" />
       </v-toolbar-title>
       <v-spacer />
       <v-btn
         v-if="false"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-chart-timeline-variant-shimmer' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-chart-timeline-variant-shimmer' : null"
         color="tertiary"
         :variant="buttonVariant"
-        :text="$t('toolbars.database.dashboard.permanent') + ($vuetify.display.lgAndUp ? ' ' + $t('toolbars.database.dashboard.xl') : '')" />
+        :text="$t('toolbars.database.dashboard.permanent') + ($vuetify.display.mdAndUp ? ' ' + $t('toolbars.database.dashboard.xl') : '')" />
       <v-btn
         v-if="canCreateTable"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-table-large-plus' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-table-large-plus' : null"
         color="secondary"
         variant="flat"
-        :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-table.xl') + ' ' : '') + $t('toolbars.database.create-table.permanent')"
+        :text="($vuetify.display.mdAndUp ? $t('toolbars.database.create-table.xl') + ' ' : '') + $t('toolbars.database.create-table.permanent')"
         class="mr-2"
         :to="`/database/${$route.params.database_id}/table/create/dataset`" />
       <v-btn
         v-if="canCreateSubset"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-wrench' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-wrench' : null"
         color="secondary"
         variant="flat"
-        :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-subset.xl') + ' ' : '') + $t('toolbars.database.create-subset.permanent')"
+        :text="($vuetify.display.mdAndUp ? $t('toolbars.database.create-subset.xl') + ' ' : '') + $t('toolbars.database.create-subset.permanent')"
         class="mr-2 white--text"
         :to="`/database/${$route.params.database_id}/subset/create`" />
       <v-btn
         v-if="canCreateView"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-view-carousel-outline' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-view-carousel-outline' : null"
         color="secondary"
         variant="flat"
-        :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')"
+        :text="($vuetify.display.mdAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')"
         class="mr-2 white--text"
         :to="`/database/${$route.params.database_id}/view/create`" />
       <v-btn
         v-if="canCreateIdentifier"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-identifier' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-identifier' : null"
         color="primary"
         variant="flat"
-        :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-pid.xl') + ' ' : '') + $t('toolbars.database.create-pid.permanent')"
+        :text="($vuetify.display.mdAndUp ? $t('toolbars.database.create-pid.xl') + ' ' : '') + $t('toolbars.database.create-pid.permanent')"
         class="mr-2"
         :to="`/database/${$route.params.database_id}/persist`" />
       <template v-slot:extension>
@@ -95,8 +84,12 @@
 <script>
 import { useCacheStore } from '@/stores/cache'
 import { useUserStore } from '@/stores/user'
+import ResourceStatus from '@/components/ResourceStatus.vue'
 
 export default {
+  components: {
+    ResourceStatus
+  },
   data () {
     return {
       tab: null,
diff --git a/dbrepo-ui/components/dialogs/DropTable.vue b/dbrepo-ui/components/dialogs/DropTable.vue
deleted file mode 100644
index d465c882d0529a817be21f836ea82739fc30155b..0000000000000000000000000000000000000000
--- a/dbrepo-ui/components/dialogs/DropTable.vue
+++ /dev/null
@@ -1,114 +0,0 @@
-<template>
-  <div>
-    <v-form ref="form" v-model="valid" autocomplete="off" @submit.prevent="submit">
-      <v-card
-        :title="$t('pages.table.subpages.drop.title') + ' ' + table.internal_name"
-        variant="elevated">
-        <v-card-text>
-          <v-row dense>
-            <v-col>
-              <span>
-                {{ $t('pages.table.subpages.drop.warning.prefix') }}
-              </span>
-              &nbsp;<code class="code-key">{{ table.internal_name }}</code>&nbsp;
-              <span>
-                {{ $t('pages.table.subpages.drop.warning.suffix') }}
-              </span>
-            </v-col>
-          </v-row>
-          <v-row>
-            <v-col>
-              <v-text-field
-                id="confirm"
-                v-model="confirm"
-                name="confirm"
-                persistent-hint
-                :variant="inputVariant"
-                :label="$t('pages.table.subpages.drop.name.label')"
-                :hint="$t('pages.table.subpages.drop.name.hint')"
-                autofocus
-                required />
-            </v-col>
-          </v-row>
-        </v-card-text>
-        <v-card-actions>
-          <v-spacer />
-          <v-btn
-            :variant="buttonVariant"
-            :text="$t('navigation.cancel')"
-            @click="cancel" />
-          <v-btn
-            color="error"
-            variant="flat"
-            :text="$t('navigation.delete')"
-            :loading="loadingDelete"
-            :disabled="confirm !== table.internal_name"
-            type="submit"
-            @click="dropTable" />
-        </v-card-actions>
-      </v-card>
-    </v-form>
-  </div>
-</template>
-
-<script>
-import { useCacheStore } from '@/stores/cache'
-
-export default {
-  data () {
-    return {
-      confirm: null,
-      loadingDelete: false,
-      valid: false,
-      cacheStore: useCacheStore()
-    }
-  },
-  computed: {
-    table () {
-      return this.cacheStore.getTable
-    },
-    database () {
-      return this.cacheStore.getDatabase
-    },
-    inputVariant () {
-      const runtimeConfig = useRuntimeConfig()
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal
-    },
-    buttonVariant () {
-      const runtimeConfig = useRuntimeConfig()
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
-    }
-  },
-  methods: {
-    submit () {
-      this.$refs.form.validate()
-    },
-    cancel () {
-      this.$emit('close', { action: 'closed' })
-    },
-    dropTable () {
-      if (!this.table.id) {
-        return
-      }
-      this.loadingDelete = true
-      const tableService = useTableService()
-      tableService.remove(this.database.id, this.table.id)
-        .then(() => {
-          console.info('Deleted table with id ', this.table.id)
-          this.cacheStore.reloadDatabase()
-          const toast = useToastInstance()
-          toast.success('Successfully deleted table with id ' + this.table.id)
-          this.$router.push(`/database/${this.$route.params.database_id}/table`)
-        })
-        .finally(() => {
-          this.loadingDelete = false
-        })
-    }
-  }
-}
-</script>
-<style scoped>
-.code-key {
-  padding: 2px 4px;
-}
-</style>
diff --git a/dbrepo-ui/components/dialogs/EditAccess.vue b/dbrepo-ui/components/dialogs/EditAccess.vue
index 039b1c40e8e5d241b1d52f6145affad898581702..856a442592bf9b40000b9295c8b7ed9bc17f6b78 100644
--- a/dbrepo-ui/components/dialogs/EditAccess.vue
+++ b/dbrepo-ui/components/dialogs/EditAccess.vue
@@ -172,11 +172,14 @@ export default {
       accessService.remove(this.$route.params.database_id, this.localUserId)
         .then(() => {
           const toast = useToastInstance()
-          toast.success(this.$t('success.access.revoked'))
+          toast.success(this.$t('success.access.revoked', { access: this.modify.type }))
           this.$emit('close-dialog', { success: true })
         })
         .catch(({code, message}) => {
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(message)
         })
         .finally(() => {
@@ -185,14 +188,17 @@ export default {
     },
     modifyAccess () {
       const accessService = useAccessService()
-      accessService.modify(this.$route.params.database_id, this.localUserId, this.modify)
+      accessService.update(this.$route.params.database_id, this.localUserId, this.modify)
         .then(() => {
           const toast = useToastInstance()
-          toast.success(this.$t('success.access.modified'))
+          toast.success(this.$t('success.access.modified', { access: this.modify.type }))
           this.$emit('close-dialog', { success: true })
         })
         .catch(({code, message}) => {
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(message)
         })
         .finally(() => {
@@ -204,11 +210,14 @@ export default {
       accessService.create(this.$route.params.database_id, this.localUserId, this.modify)
         .then(() => {
           const toast = useToastInstance()
-          toast.success(this.$t('success.access.created'))
+          toast.success(this.$t('success.access.created', { access: this.modify.type }))
           this.$emit('close-dialog', { success: true })
         })
         .catch(({code, message}) => {
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(message)
         })
         .finally(() => {
@@ -220,10 +229,13 @@ export default {
       const userService = useUserService()
       userService.findAll()
         .then((users) => {
-          this.users = users.filter(u => u.username !== this.database.creator.username)
+          this.users = users.filter(u => u.id !== this.database.owner.id)
         })
         .catch(({code}) => {
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
         })
         .finally(() => {
diff --git a/dbrepo-ui/components/dialogs/EditTuple.vue b/dbrepo-ui/components/dialogs/EditTuple.vue
index ea0bfb3c5b9161a907c2ca61544daafb737dd128..3290d230d1bf216bd2f212c4fbe9c683901e4b96 100644
--- a/dbrepo-ui/components/dialogs/EditTuple.vue
+++ b/dbrepo-ui/components/dialogs/EditTuple.vue
@@ -27,7 +27,7 @@
                 type="number">
                 <template
                   v-slot:append>
-                  {{ column.column_type.toUpperCase() }}
+                  {{ column.type.toUpperCase() }}
                   <NuxtLink
                     target="_blank"
                     class="ml-2"
@@ -61,7 +61,7 @@
                 type="text">
                 <template
                   v-slot:append>
-                  {{ column.column_type.toUpperCase() }}
+                  {{ column.type.toUpperCase() }}
                   <NuxtLink
                     target="_blank"
                     class="ml-2"
@@ -94,7 +94,7 @@
                 type="number">
                 <template
                   v-slot:append>
-                  {{ column.column_type.toUpperCase() }}
+                  {{ column.type.toUpperCase() }}
                   <NuxtLink
                     target="_blank"
                     class="ml-2"
@@ -126,7 +126,7 @@
                 :hint="hint(column)">
                 <template
                   v-slot:append>
-                  {{ column.column_type.toUpperCase() }}
+                  {{ column.type.toUpperCase() }}
                   <NuxtLink
                     target="_blank"
                     class="ml-2"
@@ -161,7 +161,7 @@
                 :items="isSet(column) ? column.sets : column.enums">
                 <template
                   v-slot:append>
-                  {{ column.column_type.toUpperCase() }}
+                  {{ column.type.toUpperCase() }}
                   <NuxtLink
                     target="_blank"
                     class="ml-2"
@@ -192,7 +192,7 @@
                 :clearable="!required(column)">
                 <template
                   v-slot:append>
-                  {{ column.column_type.toUpperCase() }}
+                  {{ column.type.toUpperCase() }}
                   <NuxtLink
                     target="_blank"
                     class="ml-2"
@@ -221,7 +221,7 @@
                 :hint="hint(column)">
                 <template
                   v-slot:append>
-                  {{ column.column_type.toUpperCase() }}
+                  {{ column.type.toUpperCase() }}
                   <NuxtLink
                     target="_blank"
                     class="ml-2"
@@ -313,6 +313,8 @@ export default {
       oldTuple: null,
       error: false,
       menu: false,
+      loadContainer: false,
+      container: null,
       bools: [
         { title: 'true', value: true },
         { title: 'false', value: false }
@@ -321,6 +323,7 @@ export default {
     }
   },
   mounted() {
+    this.fetchContainer()
     this.$refs.form.validate()
     this.oldTuple = Object.assign({}, this.tuple)
   },
@@ -328,11 +331,20 @@ export default {
     database () {
       return this.cacheStore.getDatabase
     },
+    table () {
+      return this.cacheStore.getTable
+    },
     columnTypes () {
-      if (!this.database) {
+      if (!this.container) {
+        return []
+      }
+      return this.container.image.data_types
+    },
+    primaryKeyColumns () {
+      if (!this.table) {
         return []
       }
-      return this.database.container.image.data_types
+      return this.table.constraints.primary_key.map(pk => pk.column)
     },
     title () {
       return (this.edit ? this.$t('toolbars.table.data.edit') : this.$t('toolbars.table.data.add')) + ' ' + this.$t('toolbars.table.data.tuple')
@@ -360,7 +372,7 @@ export default {
       if (!is_null_allowed) {
         hint += this.$t('pages.table.subpages.data.required.hint')
       }
-      if (column.column_type === 'sequence') {
+      if (column.type === 'sequence') {
         hint += ' ' + this.$t('pages.table.subpages.data.auto.hint')
       }
       if (is_primary_key) {
@@ -371,47 +383,47 @@ export default {
       }
       return hint
     },
-    documentationLink ({column_type}) {
-      const filter = this.columnTypes.filter(t => t.value === column_type)
+    documentationLink ({type}) {
+      const filter = this.columnTypes.filter(t => t.value === type)
       if (filter.length !== 1) {
         return null
       }
       return filter[0].documentation
     },
-    formatHint ({column_type}) {
-      const filter = this.columnTypes.filter(t => t.value === column_type)
+    formatHint ({type}) {
+      const filter = this.columnTypes.filter(t => t.value === type)
       if (filter.length !== 1) {
         return null
       }
       return filter[0].data_hint
     },
     isTextField (column) {
-      const { column_type } = column
-      return ['char', 'varchar', 'tinytext', 'mediumtext'].includes(column_type)
+      const { type } = column
+      return ['char', 'varchar', 'tinytext', 'mediumtext'].includes(type)
     },
     isTextArea (column) {
-      return ['text'].includes(column.column_type)
+      return ['text'].includes(column.type)
     },
     isFileField (column) {
-      return ['blob', 'longblob', 'mediumblob', 'tinyblob'].includes(column.column_type)
+      return ['blob', 'longblob', 'mediumblob', 'tinyblob'].includes(column.type)
     },
     isBoolean (column) {
-      return ['bool'].includes(column.column_type)
+      return ['bool'].includes(column.type)
     },
     isNumber (column) {
-      return ['int', 'binary', 'bit', 'tinyint', 'smallint', 'mediumint', 'bigint'].includes(column.column_type)
+      return ['int', 'binary', 'bit', 'tinyint', 'smallint', 'mediumint', 'bigint', 'serial'].includes(column.type)
     },
     isFloatingPoint (column) {
-      return ['float', 'double', 'decimal'].includes(column.column_type)
+      return ['float', 'double', 'decimal'].includes(column.type)
     },
     isEnum (column) {
-      return column.column_type === 'enum'
+      return column.type === 'enum'
     },
     isSet (column) {
-      return column.column_type === 'set'
+      return column.type === 'set'
     },
     isTimeField (column) {
-      return ['date', 'datetime', 'timestamp', 'time', 'year'].includes(column.column_type)
+      return ['date', 'datetime', 'timestamp', 'time', 'year'].includes(column.type)
     },
     rules (column) {
       if (column.is_null_allowed) {
@@ -419,7 +431,7 @@ export default {
       }
       const rules = []
       rules.push(v => v !== null || this.$t('validation.required'))
-      if (column.column_type === 'decimal' || column.column_type === 'double') {
+      if (column.type === 'decimal' || column.type === 'double') {
         rules.push(v => !(!v || v.split('.')[0].length > column.size) || `${this.$t('pages.table.subpages.data.float.max')} ${column.size} ${this.$t('pages.table.subpages.data.float.before')}`)
         rules.push(v => !(!v || (column.d && v.split('.')[1].length > column.d)) || `${this.$t('pages.table.subpages.data.float.max')} ${column.d} ${this.$t('pages.table.subpages.data.float.after')}`)
       }
@@ -439,19 +451,11 @@ export default {
     },
     updateTuple () {
       const constraints = {}
-      if (this.table.constraints.primary_key.length > 0) {
-        this.table.constraints.primary_key
-          .forEach((pk) => {
-            constraints[pk.column.internal_name] = this.oldTuple[pk.column.internal_name]
-          })
-        console.debug('table has primary key: set update tuple constraints', constraints)
-      } else {
-        this.table.columns
-          .forEach((column) => {
-            constraints[column.internal_name] = this.oldTuple[column.internal_name]
-          })
-        console.debug('table does not have a primary key: set update tuple constraints', constraints)
-      }
+      this.primaryKeyColumns
+        .forEach((pk) => {
+          constraints[pk.internal_name] = this.oldTuple[pk.internal_name]
+        })
+      console.debug('table has primary key: set update tuple constraints', constraints)
       const tupleService = useTupleService()
       this.loading = true
       tupleService.update(this.$route.params.database_id, this.$route.params.table_id, { data: this.tuple, keys: constraints })
@@ -462,9 +466,12 @@ export default {
           this.loading = false
         })
         .catch(({message}) => {
+          this.loading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(message)
-          this.loading = false
         })
         .finally(() => {
           this.loading = false
@@ -491,10 +498,13 @@ export default {
           this.$emit('close', { success: true })
           this.loading = false
         })
-        .catch(({message}) => {
+        .catch(({code, message}) => {
+          this.loading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(message)
-          this.loading = false
         })
         .finally(() => {
           this.loading = false
@@ -504,6 +514,29 @@ export default {
       const toast = useToastInstance()
       toast.success(this.$t('success.upload.blob'))
       this.tuple[column.internal_name] = s3key
+    },
+    fetchContainer () {
+      if (!this.database) {
+        return
+      }
+      this.loadContainer = true
+      const containerService = useContainerService()
+      containerService.findOne(this.database.container.id)
+        .then((container) => {
+          this.container = container
+          this.loadContainer = false
+        })
+        .catch(({code, message}) => {
+          this.loadContainer = false
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(message)
+        })
+        .finally(() => {
+          this.loadContainer = false
+        })
     }
   }
 }
diff --git a/dbrepo-ui/components/dialogs/UpdateTable.vue b/dbrepo-ui/components/dialogs/UpdateTable.vue
deleted file mode 100644
index 6e4e07182c12c480b1f4c5d4da3756d3e111164a..0000000000000000000000000000000000000000
--- a/dbrepo-ui/components/dialogs/UpdateTable.vue
+++ /dev/null
@@ -1,167 +0,0 @@
-<template>
-  <div>
-    <v-form
-      ref="form"
-      v-model="valid"
-      autocomplete="off"
-      @submit.prevent="submit">
-      <v-card
-        :title="$t('pages.view.visibility.title')">
-        <v-card-text>
-          <v-row>
-            <v-col>
-              <v-textarea
-                v-model="modify.description"
-                rows="2"
-                :rules="[
-                  v => (!!v || v.length <= 180) || ($t('validation.max-length') + 180),
-                ]"
-                clearable
-                counter="180"
-                persistent-counter
-                persistent-hint
-                :variant="inputVariant"
-                :hint="$t('pages.table.subpages.import.description.hint')"
-                :label="$t('pages.table.subpages.import.description.label')"/>
-            </v-col>
-          </v-row>
-          <v-row
-            dense>
-            <v-col
-              md="6">
-              <v-select
-                v-model="modify.is_public"
-                :items="visibilities"
-                persistent-hint
-                :variant="inputVariant"
-                required
-                :rules="[
-                  v => v !== null || $t('validation.required')
-                ]"
-                :label="$t('pages.database.subpages.create.data.label')"
-                :hint="$t('pages.database.subpages.create.data.hint')" />
-            </v-col>
-            <v-col
-              md="6">
-              <v-select
-                v-model="modify.is_schema_public"
-                :items="visibilities"
-                persistent-hint
-                :variant="inputVariant"
-                required
-                :rules="[
-                  v => v !== null || $t('validation.required')
-                ]"
-                :label="$t('pages.database.subpages.create.schema.label')"
-                :hint="$t('pages.database.subpages.create.schema.hint')" />
-            </v-col>
-          </v-row>
-        </v-card-text>
-        <v-card-actions>
-          <v-spacer />
-          <v-btn
-            :variant="buttonVariant"
-            :text="$t('navigation.cancel')"
-            @click="cancel" />
-          <v-btn
-            id="database"
-            variant="flat"
-            :disabled="!valid || !isChange"
-            :color="buttonColor"
-            :loading="loading"
-            type="submit"
-            :text="$t('navigation.modify')"
-            @click="update" />
-        </v-card-actions>
-      </v-card>
-    </v-form>
-  </div>
-</template>
-
-<script>
-import { useCacheStore } from '@/stores/cache'
-
-export default {
-  props: {
-    table: {
-      type: Object,
-      default () {
-        return {
-          is_public: true,
-          is_schema_public: true,
-          description: null
-        }
-      }
-    },
-  },
-  data () {
-    return {
-      valid: false,
-      loading: false,
-      visibilities: [
-        { title: this.$t('toolbars.database.public'), value: true },
-        { title: this.$t('toolbars.database.private'), value: false },
-      ],
-      modify: {
-        description: this.table.description,
-        is_public: this.table.is_public,
-        is_schema_public: this.table.is_schema_public
-      },
-      cacheStore: useCacheStore()
-    }
-  },
-  computed: {
-    database () {
-      return this.cacheStore.getDatabase
-    },
-    inputVariant () {
-      const runtimeConfig = useRuntimeConfig()
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal
-    },
-    buttonVariant () {
-      const runtimeConfig = useRuntimeConfig()
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
-    },
-    isChange () {
-      if (this.table.description !== this.modify.description) {
-        return true
-      }
-      if (this.table.is_public !== this.modify.is_public) {
-        return true
-      }
-      return this.table.is_schema_public !== this.modify.is_schema_public
-    },
-    buttonColor () {
-      return !this.isChange ? null : 'warning'
-    }
-  },
-  methods: {
-    submit () {
-      this.$refs.form.validate()
-    },
-    cancel () {
-      this.$emit('close', { success: false })
-    },
-    update () {
-      this.loading = true
-      const tableService = useTableService()
-      tableService.update(this.$route.params.database_id, this.$route.params.table_id, this.modify)
-        .then(() => {
-          this.loading = false
-          const toast = useToastInstance()
-          toast.success(this.$t('success.table.updated'))
-          this.$emit('close', { success: true })
-          this.cacheStore.reloadTable()
-        })
-        .catch(({code}) => {
-          this.loading = false
-          const toast = useToastInstance()
-          toast.error(this.$t(code))
-        })
-        .finally(() => {
-          this.loading = false
-        })
-    }
-  }
-}
-</script>
diff --git a/dbrepo-ui/components/dialogs/ViewVisibility.vue b/dbrepo-ui/components/dialogs/ViewVisibility.vue
index 226772edd922a9d71e40a312d461c666c50e8893..7d113116674f79d6980c71096061fc965c15e93e 100644
--- a/dbrepo-ui/components/dialogs/ViewVisibility.vue
+++ b/dbrepo-ui/components/dialogs/ViewVisibility.vue
@@ -14,29 +14,29 @@
               md="6">
               <v-select
                 v-model="modify.is_public"
-                :items="visibilities"
+                :items="dataOptions"
                 persistent-hint
                 :variant="inputVariant"
                 required
                 :rules="[
                   v => v !== null || $t('validation.required')
                 ]"
-                :label="$t('pages.database.subpages.create.data.label')"
-                :hint="$t('pages.database.subpages.create.data.hint')" />
+                :label="$t('pages.database.resource.data.label')"
+                :hint="$t('pages.database.resource.data.hint', { resource: 'view' })" />
             </v-col>
             <v-col
               md="6">
               <v-select
                 v-model="modify.is_schema_public"
-                :items="visibilities"
+                :items="schemaOptions"
                 persistent-hint
                 :variant="inputVariant"
                 required
                 :rules="[
                   v => v !== null || $t('validation.required')
                 ]"
-                :label="$t('pages.database.subpages.create.schema.label')"
-                :hint="$t('pages.database.subpages.create.schema.hint')" />
+                :label="$t('pages.database.resource.schema.label')"
+                :hint="$t('pages.database.resource.schema.hint', { resource: 'view', schema: 'columns' })" />
             </v-col>
           </v-row>
         </v-card-text>
@@ -82,9 +82,13 @@ export default {
       loadingUsers: false,
       users: [],
       error: false,
-      visibilities: [
-        { title: this.$t('toolbars.database.public'), value: true },
-        { title: this.$t('toolbars.database.private'), value: false },
+      dataOptions: [
+        { title: this.$t('pages.database.resource.data.enabled'), value: true },
+        { title: this.$t('pages.database.resource.data.disabled'), value: false },
+      ],
+      schemaOptions: [
+        { title: this.$t('pages.database.resource.schema.enabled'), value: true },
+        { title: this.$t('pages.database.resource.schema.disabled'), value: false },
       ],
       modify: {
         is_public: this.view.is_public,
@@ -122,25 +126,6 @@ export default {
     cancel () {
       this.$emit('close', { success: false })
     },
-    updateVisibility () {
-      this.loading = true
-      const viewService = useViewService()
-      viewService.update(this.$route.params.database_id, this.$route.params.view_id, this.modify)
-        .then(() => {
-          this.loading = false
-          const toast = useToastInstance()
-          toast.success(this.$t('success.view.modified'))
-          this.$emit('close', { success: true })
-        })
-        .catch(({code, message}) => {
-          this.loading = false
-          const toast = useToastInstance()
-          toast.error(message)
-        })
-        .finally(() => {
-          this.loading = false
-        })
-    }
   }
 }
 </script>
diff --git a/dbrepo-ui/components/identifier/Citation.vue b/dbrepo-ui/components/identifier/Citation.vue
index 7cd99194b099987f3853fde4afd7bf92f463dd38..5722351f0ab1d3697783f3f0f82c54c5452981d4 100644
--- a/dbrepo-ui/components/identifier/Citation.vue
+++ b/dbrepo-ui/components/identifier/Citation.vue
@@ -68,9 +68,12 @@ export default {
           this.loading = false
         })
         .catch(({code, message}) => {
+          this.loading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(`${code}: ${message}`))
-          this.loading = false
         })
     }
   }
diff --git a/dbrepo-ui/components/identifier/Select.vue b/dbrepo-ui/components/identifier/Select.vue
index bacbab44149b75289a81741afc52fd4c2983283f..e1de8d1b26bc021bdf54d98c106d7e79cd8ec65d 100644
--- a/dbrepo-ui/components/identifier/Select.vue
+++ b/dbrepo-ui/components/identifier/Select.vue
@@ -86,7 +86,7 @@ export default {
       if (!this.user) {
         return this.identifiers.filter(i => i.status === 'published')
       }
-      return this.identifiers.filter(i => i.status === 'published' || i.creator.id === this.user.id)
+      return this.identifiers.filter(i => i.status === 'published' || i.owner.id === this.user.id)
     },
     listVariant () {
       const runtimeConfig = useRuntimeConfig()
diff --git a/dbrepo-ui/components/search/AdvancedSearch.vue b/dbrepo-ui/components/search/AdvancedSearch.vue
index 13de402e019f2a791f0233c4fc611164e46e20d8..8197cd3fb8a960f44440cc8238c0a1a42cb7f57e 100644
--- a/dbrepo-ui/components/search/AdvancedSearch.vue
+++ b/dbrepo-ui/components/search/AdvancedSearch.vue
@@ -75,14 +75,14 @@
                 :variant="inputVariant"
                 :label="field.attr_friendly_name" />
               <v-text-field
-                v-if="(field.type === 'keyword' && field.attr_name !== 'column_type') || field.type === 'text' || field.type === 'date'"
+                v-if="(field.type === 'keyword' && field.attr_name !== 'type') || field.type === 'text' || field.type === 'date'"
                 v-model="advancedSearchData[field.attr_name]"
                 type="text"
                 :variant="inputVariant"
                 :label="field.attr_friendly_name"
                 clearable />
               <v-select
-                v-if="field.type === 'keyword' && field.attr_name === 'column_type'"
+                v-if="field.type === 'keyword' && field.attr_name === 'type'"
                 v-model="advancedSearchData[field.attr_name]"
                 :items="columnTypes"
                 item-value="value"
@@ -384,8 +384,8 @@ export default {
       this.loading = true
       const searchService = useSearchService()
       searchService.general_search(this.searchType, this.advancedSearchData)
-        .then(({results, type}) => {
-          this.$emit('search-result', {results, type})
+        .then((results) => {
+          this.$emit('search-result', results)
         })
         .finally(() => {
           this.loading = false
@@ -443,7 +443,7 @@ export default {
         return
       }
       this.resetAdvancedSearchFields()
-      this.$emit('search-result', { results: [], type: this.searchType })
+      this.$emit('search-result', [])
       const searchService = useSearchService()
       this.loadingFields = true
       searchService.fields(this.searchType)
diff --git a/dbrepo-ui/components/subset/Builder.vue b/dbrepo-ui/components/subset/Builder.vue
index 2a14b8f474a8f7a3cc0e38302e3ebad0e0df5a7d..2895476a59ea37a7db0cd768050012bbff9f6cff 100644
--- a/dbrepo-ui/components/subset/Builder.vue
+++ b/dbrepo-ui/components/subset/Builder.vue
@@ -68,7 +68,7 @@
               md="4">
               <v-select
                 v-model="view.is_public"
-                :items="visibilities"
+                :items="dataOptions"
                 persistent-hint
                 :variant="inputVariant"
                 required
@@ -76,14 +76,14 @@
                 :rules="[
                   v => !!v || $t('validation.required')
                 ]"
-                :label="$t('pages.database.subpages.create.data.label')"
-                :hint="$t('pages.database.subpages.create.data.hint')" />
+                :label="$t('pages.database.resource.data.label')"
+                :hint="$t('pages.database.resource.data.hint')" />
             </v-col>
             <v-col
               md="4">
               <v-select
                 v-model="view.is_schema_public"
-                :items="visibilities"
+                :items="schemaOptions"
                 persistent-hint
                 :variant="inputVariant"
                 required
@@ -91,8 +91,8 @@
                 :rules="[
                   v => !!v || $t('validation.required')
                 ]"
-                :label="$t('pages.database.subpages.create.schema.label')"
-                :hint="$t('pages.database.subpages.create.schema.hint')" />
+                :label="$t('pages.database.resource.schema.label')"
+                :hint="$t('pages.database.resource.schema.hint', { resource: 'subset', schema: 'query' })" />
             </v-col>
           </v-row>
           <v-window
@@ -332,9 +332,13 @@ export default {
       columns: [],
       timestamp: null,
       executeDifferentTimestamp: false,
-      visibilities: [
-        { title: this.$t('toolbars.database.public'), value: true },
-        { title: this.$t('toolbars.database.private'), value: false },
+      dataOptions: [
+        { title: this.$t('pages.database.resource.data.enabled'), value: true },
+        { title: this.$t('pages.database.resource.data.disabled'), value: false },
+      ],
+      schemaOptions: [
+        { title: this.$t('pages.database.resource.schema.enabled'), value: true },
+        { title: this.$t('pages.database.resource.schema.disabled'), value: false },
       ],
       tableDetails: null,
       resultId: null,
@@ -485,9 +489,12 @@ export default {
           this.loadingColumns = false
         })
         .catch(({code}) => {
+          this.loadingColumns = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
-          this.loadingColumns = false
         })
     },
     validViewName (name) {
diff --git a/dbrepo-ui/components/subset/Results.vue b/dbrepo-ui/components/subset/Results.vue
index e9e3c0deb5cddf119cab770014ff085d71b6df94..e558186daf15c186dfdc085a844799815e3756a4 100644
--- a/dbrepo-ui/components/subset/Results.vue
+++ b/dbrepo-ui/components/subset/Results.vue
@@ -2,12 +2,15 @@
   <div>
     <v-data-table-server
       flat
+      v-model="selection"
       :headers="headers"
       :loading="loading || loadingCount || loadingExecute"
       :options="options"
       :items="result.rows"
       :items-length="total"
       :footer-props="footerProps"
+      :show-select="select"
+      return-object
       :items-per-page-options="footerProps.itemsPerPageOptions"
       @update:options="updateOptions" />
   </div>
@@ -24,6 +27,10 @@ export default {
       type: Boolean,
       default: () => false
     },
+    select: {
+      type: Boolean,
+      default: () => false
+    },
     timestamp: {
       type: String,
       default: () => new Date().toISOString()
@@ -35,6 +42,7 @@ export default {
       loadingExecute: false,
       resultId: null,
       id: null,
+      selection: null,
       result: {
         headers: [],
         rows: []
@@ -64,6 +72,11 @@ export default {
         this.reExecute(this.id)
       },
       deep: true
+    },
+    selection: {
+      handler () {
+        this.$emit('selection', this.selection)
+      }
     }
   },
   methods: {
@@ -224,6 +237,9 @@ export default {
       this.options.page = page
       this.options.itemsPerPage = itemsPerPage
       this.reExecute(this.id)
+    },
+    resetSelection () {
+      this.selection = []
     }
   }
 }
diff --git a/dbrepo-ui/components/subset/SubsetList.vue b/dbrepo-ui/components/subset/SubsetList.vue
index f57dc68a88fd77c06b858b423565b4ccac7d46f0..62a456eb793ae1f95a49fda7569e8fb8799b9ccb 100644
--- a/dbrepo-ui/components/subset/SubsetList.vue
+++ b/dbrepo-ui/components/subset/SubsetList.vue
@@ -26,21 +26,6 @@
             :to="link(item)"
             :href="link(item)">
             <template v-slot:append>
-              <v-chip
-                v-if="database.is_public"
-                size="small"
-                class="ml-2"
-                color="success"
-                :text="$t('toolbars.database.public')"
-                variant="outlined" />
-              <v-chip
-                v-if="!database.is_public"
-                size="small"
-                class="ml-2"
-                :color="colorVariant"
-                variant="outlined"
-                :text="$t('toolbars.database.private')"
-                flat />
               <v-tooltip
                 v-if="hasPublishedIdentifier(item)"
                 :text="$t('pages.identifier.pid.title')"
@@ -101,7 +86,11 @@ export default {
       queryService.findAll(this.$route.params.database_id, true)
         .then((subsets) => {
           this.loadingSubsets = false
-          this.subsets = subsets
+          this.subsets = subsets.map(subset => {
+            subset.is_public = this.database.is_public
+            subset.is_schema_public = this.database.is_schema_public
+            return subset
+          })
         })
         .catch(({code}) => {
           this.loadingSubsets = false
diff --git a/dbrepo-ui/components/subset/SubsetToolbar.vue b/dbrepo-ui/components/subset/SubsetToolbar.vue
index 0fc5be7c88e0db232ba76bf91cec1f3c351ab5be..405bb6d00fe4989a2e73c6abb2bf518194ff5b1a 100644
--- a/dbrepo-ui/components/subset/SubsetToolbar.vue
+++ b/dbrepo-ui/components/subset/SubsetToolbar.vue
@@ -3,7 +3,6 @@
     <v-toolbar
       flat>
       <v-btn
-        class="mr-2"
         variant="plain"
         size="small"
         icon="mdi-arrow-left"
@@ -17,7 +16,7 @@
         :loading="loadingSave"
         color="secondary"
         variant="flat"
-        class="mb-1 ml-2"
+        class="mr-2"
         :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-star' : null"
         :text="$t('toolbars.subset.save.permanent')"
         @click.stop="save" />
@@ -26,15 +25,15 @@
         :loading="loadingSave"
         color="warning"
         variant="flat"
-        class="mb-1 ml-2"
+        class="mr-2"
         :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-star-off' : null"
         :text="$t('toolbars.subset.unsave.permanent')"
         @click.stop="forget" />
       <v-btn
         v-if="canGetPid"
-        class="mb-1 ml-2"
         color="primary"
         variant="flat"
+        class="mr-2"
         :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null"
         :disabled="!executionUTC"
         :to="`/database/${$route.params.database_id}/subset/${$route.params.subset_id}/persist`">
@@ -73,7 +72,6 @@ export default {
       loading: false,
       loadingSave: false,
       downloadLoading: false,
-      subset: null,
       userStore: useUserStore(),
       cacheStore: useCacheStore()
     }
@@ -97,11 +95,14 @@ export default {
     roles () {
       return this.userStore.getRoles
     },
+    subset () {
+      return this.cacheStore.getSubset
+    },
     identifiers () {
-      if (!this.database || !this.database.subsets || this.database.subsets.length === 0) {
+      if (!this.subset) {
         return []
       }
-      return this.database.subsets.filter(s => s.query_id === Number(this.$route.params.subset_id))
+      return this.subset.identifiers
     },
     canViewData () {
       if (!this.database) {
@@ -157,7 +158,7 @@ export default {
       if (!this.user || !this.subset || !this.database) {
         return false
       }
-      return this.database.owner.id === this.user.id || (this.subset.creator.id === this.user.id && this.hasReadAccess)
+      return this.database.owner.id === this.user.id || (this.subset.owner.id === this.user.id && this.hasReadAccess)
     },
     title () {
       if (!this.identifier) {
@@ -171,12 +172,6 @@ export default {
       return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
     }
   },
-  mounted () {
-    /* load subset metadata */
-    if (!this.subset) {
-      this.loadSubset()
-    }
-  },
   methods: {
     save () {
       this.loadingSave = true
@@ -206,20 +201,6 @@ export default {
         .finally(() => {
           this.loadingSave = false
         })
-    },
-    loadSubset () {
-      this.loading = true
-      const queryService = useQueryService()
-      queryService.findOne(this.$route.params.database_id, this.$route.params.subset_id)
-        .then((subset) => {
-          this.subset = subset
-        })
-        .catch(() => {
-          this.loading = false
-        })
-        .finally(() => {
-          this.loading = false
-        })
     }
   }
 }
diff --git a/dbrepo-ui/components/table/TableHistory.vue b/dbrepo-ui/components/table/TableHistory.vue
index 34d45248e7341b9a5b8b3af1c2d799e4408e6ecb..ccc270c46c22de4933c76a448a9e7db95a2f6a10 100644
--- a/dbrepo-ui/components/table/TableHistory.vue
+++ b/dbrepo-ui/components/table/TableHistory.vue
@@ -173,9 +173,12 @@ export default {
           }
         })
         .catch(({message}) => {
+          this.loading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(message)
-          this.loading = false
         })
     }
   }
diff --git a/dbrepo-ui/components/table/TableImport.vue b/dbrepo-ui/components/table/TableImport.vue
index c0e4d6f9348d1cebe920fcd1e55f64e504cada16..580839974eeab5525fb090dcc6bdcea977846fec 100644
--- a/dbrepo-ui/components/table/TableImport.vue
+++ b/dbrepo-ui/components/table/TableImport.vue
@@ -434,10 +434,12 @@ export default {
           this.loadingImport = false
         })
         .catch(({code, message}) => {
+          this.loadingImport = false
           const toast = useToastInstance()
-          console.error(code, message)
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(`${this.$t(code)}: ${message}`)
-          this.loadingImport = false
         })
         .finally(() => {
           this.loadingImport = false
diff --git a/dbrepo-ui/components/table/TableList.vue b/dbrepo-ui/components/table/TableList.vue
index 8bb77b2b5a8971e3a0d8dbe557e9d5d849f492c1..234470076db8bb400542fdebe4274b2eefad8e62 100644
--- a/dbrepo-ui/components/table/TableList.vue
+++ b/dbrepo-ui/components/table/TableList.vue
@@ -19,21 +19,8 @@
           :subtitle="table.description ? table.description : ''"
           :to="`/database/${$route.params.database_id}/table/${table.id}/info`">
           <template v-slot:append>
-            <v-chip
-              v-if="table && table.is_public"
-              size="small"
-              class="ml-2"
-              color="success"
-              :text="$t('toolbars.database.public')"
-              variant="outlined" />
-            <v-chip
-              v-if="table && !table.is_public"
-              size="small"
-              class="ml-2"
-              :color="colorVariant"
-              variant="outlined"
-              :text="$t('toolbars.database.private')"
-              flat />
+            <ResourceStatus
+              :resource="table" />
             <v-tooltip
               v-if="hasPublishedIdentifier(table)"
               :text="$t('pages.identifier.pid.title')"
@@ -69,7 +56,7 @@ export default {
       dialogDelete: false,
       headers: [
         { value: 'name', title: 'Name' },
-        { value: 'column_type', title: 'Type' },
+        { value: 'type', title: 'Type' },
         { value: 'column_concept', title: 'Concept' },
         { value: 'column_unit', title: 'Unit' },
         { value: 'is_primary_key', title: 'Primary Key' },
diff --git a/dbrepo-ui/components/table/TableSchema.vue b/dbrepo-ui/components/table/TableSchema.vue
index e9cb03c6173f334866a95e18bec220c64258fbe9..24c4fcd6823cd8d2bf33d3be5d61217978798f5c 100644
--- a/dbrepo-ui/components/table/TableSchema.vue
+++ b/dbrepo-ui/components/table/TableSchema.vue
@@ -281,6 +281,9 @@ export default {
         .catch(({code}) => {
           this.loadingColumnTypes = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
         })
     },
diff --git a/dbrepo-ui/components/table/TableToolbar.vue b/dbrepo-ui/components/table/TableToolbar.vue
index b7d358d2492bd01aa8eddc479405c660784f66c6..9821cb6fe4bbbbcfb4ad5de1df1160fedb84c722 100644
--- a/dbrepo-ui/components/table/TableToolbar.vue
+++ b/dbrepo-ui/components/table/TableToolbar.vue
@@ -9,76 +9,49 @@
       <v-toolbar-title
         v-if="table">
         <v-skeleton-loader
-          v-if="!table && $vuetify.display.lgAndUp"
+          v-if="!table && $vuetify.display.mdAndUp"
           type="subtitle"
           width="200" />
         <span
-          v-if="table && $vuetify.display.lgAndUp">
+          class="mr-2"
+          v-if="table && $vuetify.display.mdAndUp">
           {{ table.name }}
         </span>
-        <v-chip
-          v-if="table && table.is_public"
-          size="small"
-          class="ml-2"
-          color="success"
-          :text="$t('toolbars.database.public')"
-          variant="outlined" />
-        <v-chip
-          v-if="table && !table.is_public"
-          size="small"
-          class="ml-2"
-          :color="colorVariant"
-          variant="outlined"
-          :text="$t('toolbars.database.private')"
-          flat />
+        <ResourceStatus
+          :size="$vuetify.display.mdAndUp ? 'small' : 'default'"
+          :resource="table" />
       </v-toolbar-title>
       <v-spacer />
       <v-btn
         v-if="canImportCsv"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-cloud-upload' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-cloud-upload' : null"
         color="tertiary"
         :variant="buttonVariant"
-        :text="$t('toolbars.database.import-csv.permanent') + ($vuetify.display.lgAndUp ? ' ' + $t('toolbars.database.import-csv.xl') : '')"
+        :text="$t('toolbars.database.import-csv.permanent') + ($vuetify.display.mdAndUp ? ' ' + $t('toolbars.database.import-csv.xl') : '')"
         class="mr-2"
         :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/import`" />
       <v-btn
         v-if="canExecuteQuery"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-wrench' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-wrench' : null"
         color="secondary"
         variant="flat"
-        :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-subset.xl') + ' ' : '') + $t('toolbars.database.create-subset.permanent')"
+        :text="($vuetify.display.mdAndUp ? $t('toolbars.database.create-subset.xl') + ' ' : '') + $t('toolbars.database.create-subset.permanent')"
         class="mr-2"
         :to="`/database/${$route.params.database_id}/subset/create?tid=${$route.params.table_id}`" />
       <v-btn
         v-if="canCreateView"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-view-carousel' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-view-carousel' : null"
         color="secondary"
         variant="flat"
-        :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')"
+        :text="($vuetify.display.mdAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')"
         class="mr-2"
         :to="`/database/${$route.params.database_id}/view/create?tid=${$route.params.table_id}`" />
-      <v-btn
-        v-if="canUpdateTable"
-        class="mr-2"
-        variant="flat"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-table-edit' : null"
-        color="warning"
-        :text="($vuetify.display.lgAndUp ? $t('toolbars.database.update-table.xl') + ' ' : '') + $t('toolbars.database.update-table.permanent')"
-        @click="updateTableDialog = true" />
-      <v-btn
-        v-if="canDropTable"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-delete' : null"
-        color="error"
-        variant="flat"
-        :text="($vuetify.display.lgAndUp ? 'Drop ' : '') + 'Table'"
-        class="mr-2"
-        @click="dropTableDialog = true" />
       <v-btn
         v-if="canGetPid"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-content-save-outline' : null"
         color="primary"
         variant="flat"
-        :text="($vuetify.display.lgAndUp ? 'Get ' : '') + 'PID'"
+        :text="($vuetify.display.mdAndUp ? 'Get ' : '') + 'PID'"
         class="mr-2"
         :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/persist`" />
       <template v-slot:extension>
@@ -94,37 +67,24 @@
             v-if="canViewSchema"
             :text="$t('navigation.schema')"
             :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/schema`" />
+          <v-tab
+            v-if="canUpdateTable"
+            :text="$t('navigation.settings')"
+            :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/settings`" />
         </v-tabs>
       </template>
     </v-toolbar>
-    <v-dialog
-      v-model="dropTableDialog"
-      max-width="640">
-      <DropTable
-        @close="closeDelete" />
-    </v-dialog>
-    <v-dialog
-      v-model="updateTableDialog"
-      max-width="640">
-      <UpdateTable
-        :table="table"
-        @close="closeUpdate" />
-    </v-dialog>
   </div>
 </template>
 
 <script>
 import EditTuple from '@/components/dialogs/EditTuple.vue'
-import DropTable from '@/components/dialogs/DropTable.vue'
-import UpdateTable from '@/components/dialogs/UpdateTable.vue'
 import { useCacheStore } from '@/stores/cache'
 import { useUserStore } from '@/stores/user'
 
 export default {
   components: {
-    EditTuple,
-    DropTable,
-    UpdateTable
+    EditTuple
   },
   data () {
     return {
@@ -133,7 +93,6 @@ export default {
       error: false,
       edit: false,
       dropTableDialog: false,
-      updateTableDialog: false,
       cacheStore: useCacheStore(),
       userStore: useUserStore()
     }
@@ -173,16 +132,6 @@ export default {
       const userService = useUserService()
       return userService.hasReadAccess(this.access) && this.roles.includes('execute-query')
     },
-    canDropTable () {
-      if (!this.roles || !this.table || !this.user) {
-        return false
-      }
-      if (this.roles.includes('delete-foreign-table')) {
-        return true
-      }
-      const tableService = useTableService()
-      return tableService.isOwner(this.table, this.user) && this.roles.includes('delete-table') && this.table.identifiers.length === 0
-    },
     canCreateView () {
       if (!this.roles || !this.table || !this.user) {
         return false
@@ -200,7 +149,7 @@ export default {
       if (!this.user) {
         return false
       }
-      return this.hasReadAccess || this.table.owned_by === this.user.id || this.database.owner.id === this.user.id
+      return this.hasReadAccess || this.table.owner.id === this.user.id || this.database.owner.id === this.user.id
     },
     canViewSchema () {
       if (!this.table) {
@@ -212,7 +161,7 @@ export default {
       if (!this.user) {
         return false
       }
-      return this.hasReadAccess || this.table.owned_by === this.user.id || this.database.owner.id === this.user.id
+      return this.hasReadAccess || this.table.owner.id === this.user.id || this.database.owner.id === this.user.id
     },
     canImportCsv () {
       if (!this.roles || !this.table || !this.user) {
@@ -229,27 +178,6 @@ export default {
     buttonVariant () {
       const runtimeConfig = useRuntimeConfig()
       return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
-    },
-    isContrastTheme () {
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast')
-    },
-    isDarkTheme () {
-      return this.$vuetify.theme.global.name.toLowerCase().startsWith('dark')
-    },
-    colorVariant () {
-      return this.isContrastTheme ? '' : (this.isDarkTheme ? 'tertiary' : 'secondary')
-    },
-  },
-  methods: {
-    closeDelete ({success}) {
-      this.dropTableDialog = false
-      if (success) {
-        this.cacheStore.reloadDatabase()
-        this.$router.push(`/database/${this.$route.params.database_id}/table`)
-      }
-    },
-    closeUpdate () {
-      this.updateTableDialog = false
     }
   }
 }
diff --git a/dbrepo-ui/components/user/UserBadge.vue b/dbrepo-ui/components/user/UserBadge.vue
index f7bd18c60fcd64ff65538ed075d483c7a1e61b9e..9eb679de3c9345e3af6a7a23e1dea867f5042ac4 100644
--- a/dbrepo-ui/components/user/UserBadge.vue
+++ b/dbrepo-ui/components/user/UserBadge.vue
@@ -5,12 +5,15 @@
       class="mr-1"
       :orcid="orcid" />
     <span v-if="isSelf">
-      <v-badge
-        inline
-        content="you"
-        color="code">
-        {{ creatorName }}
-      </v-badge>
+      {{ creatorName }}
+      <v-chip
+        size="x-small"
+        inline>
+        {{ $t('navigation.you') }}
+        <v-icon
+          icon="mdi-account-outline"
+          end />
+      </v-chip>
     </span>
     <span
       v-else>
diff --git a/dbrepo-ui/components/view/ViewList.vue b/dbrepo-ui/components/view/ViewList.vue
index 543a8746affd8cbbed495647fc963c8db1534072..1b278a555c1154492882e92773b0f9203057c799 100644
--- a/dbrepo-ui/components/view/ViewList.vue
+++ b/dbrepo-ui/components/view/ViewList.vue
@@ -14,21 +14,8 @@
           :class="clazz(view)"
           :to="`/database/${$route.params.database_id}/view/${view.id}/info`">
           <template v-slot:append>
-            <v-chip
-              v-if="view && view.is_public"
-              size="small"
-              class="ml-2"
-              color="success"
-              :text="$t('toolbars.database.public')"
-              variant="outlined" />
-            <v-chip
-              v-if="view && !view.is_public"
-              size="small"
-              class="ml-2"
-              :color="colorVariant"
-              variant="outlined"
-              :text="$t('toolbars.database.private')"
-              flat />
+            <ResourceStatus
+              :resource="view" />
             <v-tooltip
               v-if="hasPublishedIdentifier(view)"
               :text="$t('pages.identifier.pid.title')"
diff --git a/dbrepo-ui/components/view/ViewToolbar.vue b/dbrepo-ui/components/view/ViewToolbar.vue
index 64ea3f1029407e3143bc6dd9b5cdfcb634aa9b94..6528dd3cd494c8cf878aea2149e41d1a5571e050 100644
--- a/dbrepo-ui/components/view/ViewToolbar.vue
+++ b/dbrepo-ui/components/view/ViewToolbar.vue
@@ -1,64 +1,35 @@
 <template>
   <v-toolbar flat>
     <v-btn
-      class="mr-2"
       size="small"
       icon="mdi-arrow-left"
       :to="`/database/${$route.params.database_id}/view`" />
     <v-toolbar-title
-      v-if="cachedView">
+      v-if="view">
       <span
-        v-if="$vuetify.display.lgAndUp">
+        v-if="$vuetify.display.mdAndUp"
+        class="mr-2">
         {{ title }}
       </span>
-      <v-chip
-        v-if="cachedView.is_public"
-        size="small"
-        class="ml-2"
-        color="success"
-        :text="$t('toolbars.database.public')"
-        variant="outlined" />
-      <v-chip
-        v-if="!cachedView.is_public"
-        size="small"
-        class="ml-2"
-        :color="colorVariant"
-        variant="outlined"
-        :text="$t('toolbars.database.private')"
-        flat />
+      <ResourceStatus
+        :size="$vuetify.display.mdAndUp ? 'small' : 'default'"
+        :resource="view" />
     </v-toolbar-title>
     <v-spacer />
-    <v-btn
-      v-if="canDeleteView"
-      class="mr-2"
-      variant="flat"
-      :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-delete' : null"
-      :loading="loadingDelete"
-      color="error"
-      :text="$t('navigation.delete')"
-      @click="deleteView" />
-    <v-btn
-      v-if="canUpdateVisibility"
-      class="mr-2"
-      variant="flat"
-      :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-eye' : null"
-      color="warning"
-      :text="$t('navigation.visibility')"
-      @click="updateViewDialog = true" />
     <v-btn
       v-if="canCreatePid"
       class="mr-2"
-      :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null"
+      :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-content-save-outline' : null"
       variant="flat"
       color="primary"
-      :text="($vuetify.display.lgAndUp ? $t('toolbars.view.pid.xl') + ' ' : '') + $t('toolbars.view.pid.permanent')"
+      :text="($vuetify.display.mdAndUp ? $t('toolbars.view.pid.xl') + ' ' : '') + $t('toolbars.view.pid.permanent')"
       :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/persist`" />
     <v-dialog
       v-model="updateViewDialog"
       persistent
       max-width="640">
       <ViewVisibility
-        :view="cachedView"
+        :view="view"
         @close="close" />
     </v-dialog>
     <template v-slot:extension>
@@ -69,13 +40,17 @@
           :text="$t('navigation.info')"
           :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/info`" />
         <v-tab
-          v-if="canReadData"
+          v-if="canViewData"
           :text="$t('navigation.data')"
           :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/data`" />
         <v-tab
-          v-if="canReadData"
+          v-if="canViewSchema"
           :text="$t('navigation.schema')"
           :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/schema`" />
+        <v-tab
+          v-if="canViewSettings"
+          :text="$t('navigation.settings')"
+          :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/settings`" />
       </v-tabs>
     </template>
   </v-toolbar>
@@ -113,59 +88,41 @@ export default {
       const runtimeConfig = useRuntimeConfig()
       return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
     },
-    cachedView () {
-      if (!this.database) {
-        return null
-      }
-      return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0]
+    view () {
+      return this.cacheStore.getView
     },
     canViewData () {
-      if (!this.cachedView) {
+      if (!this.view) {
         return false
       }
-      if (this.cachedView.is_public) {
+      if (this.view.is_public) {
         return true
       }
       if (!this.user) {
         return false
       }
-      return this.hasReadAccess || this.cachedView.owned_by === this.user.id || this.database.owner.id === this.user.id
+      return this.hasReadAccess || this.view.owner.id === this.user.id || this.database.owner.id === this.user.id
     },
     canViewSchema () {
-      if (!this.cachedView) {
+      if (!this.view) {
         return false
       }
-      if (this.cachedView.is_schema_public) {
+      if (this.view.is_schema_public) {
         return true
       }
       if (!this.user) {
         return false
       }
-      return this.hasReadAccess || this.cachedView.owned_by === this.user.id || this.database.owner.id === this.user.id
+      return this.hasReadAccess || this.view.owner.id === this.user.id || this.database.owner.id === this.user.id
     },
-    canDeleteView () {
-      if (!this.roles || !this.user || !this.cachedView) {
+    canViewSettings () {
+      if (!this.user || !this.view) {
         return false
       }
-      return this.roles.includes('delete-database-view') && this.cachedView.owned_by === this.user.id
-    },
-    canUpdateVisibility () {
-      if (!this.roles || !this.user || !this.cachedView) {
-        return false
-      }
-      return this.roles.includes('modify-view-visibility') && this.cachedView.owned_by === this.user.id
-    },
-    isContrastTheme () {
-      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast')
-    },
-    isDarkTheme () {
-      return this.$vuetify.theme.global.name.toLowerCase().startsWith('dark')
-    },
-    colorVariant () {
-      return this.isContrastTheme ? '' : (this.isDarkTheme ? 'tertiary' : 'secondary')
+      return this.view.owner.id === this.user.id
     },
     canCreatePid () {
-      if (!this.roles || !this.user || !this.cachedView) {
+      if (!this.roles || !this.user || !this.view) {
         return false
       }
       const userService = useUserService()
@@ -186,23 +143,11 @@ export default {
       }
       return this.access.type === 'read' ||  this.access.type === 'write_own' ||  this.access.type === 'write_all'
     },
-    canReadData () {
-      if (!this.cachedView) {
-        return false
-      }
-      if (this.cachedView.is_public) {
-        return true
-      }
-      if (!this.user) {
-        return false
-      }
-      return this.cachedView.owner.id === this.user.id || this.hasReadAccess
-    },
     identifiers () {
-      if (!this.cachedView) {
+      if (!this.view) {
         return []
       }
-      return this.cachedView.identifiers.filter(s => s.view_id === Number(this.$route.params.view_id))
+      return this.view.identifiers.filter(s => s.view_id === Number(this.$route.params.view_id))
     },
     identifier () {
       /* mount pid */
@@ -217,39 +162,10 @@ export default {
       return this.identifiers[0]
     },
     title () {
-      if (!this.cachedView) {
+      if (!this.view) {
         return null
       }
-      return this.cachedView.name
-    }
-  },
-  methods: {
-    deleteView () {
-      this.loadingDelete = true
-      const viewService = useViewService()
-      viewService.remove(this.$route.params.database_id, this.$route.params.view_id)
-        .then(() => {
-          const toast = useToastInstance()
-          toast.success(this.$t('success.view.delete'))
-          this.cacheStore.reloadDatabase()
-          this.$router.push(`/database/${this.$route.params.database_id}/view`)
-        })
-        .catch(({code, message}) => {
-          const toast = useToastInstance()
-          if (typeof code !== 'string' || typeof message !== 'string') {
-            return
-          }
-          toast.error(this.$t(code) + ": " + message)
-        })
-        .finally(() => {
-          this.loadingDelete = false
-        })
-    },
-    close ({success}) {
-      this.updateViewDialog = false
-      if (success) {
-        this.cacheStore.reloadDatabase()
-      }
+      return this.view.name
     }
   }
 }
diff --git a/dbrepo-ui/composables/container-service.ts b/dbrepo-ui/composables/container-service.ts
index 9aaf116e7efeed12f03170ec22340119d7d3341f..f1280517566165b8f190b9bd05eea3b1d4ac98f1 100644
--- a/dbrepo-ui/composables/container-service.ts
+++ b/dbrepo-ui/composables/container-service.ts
@@ -17,5 +17,21 @@ export const useContainerService = (): any => {
     })
   }
 
-  return {findAll}
+  async function findOne(containerId: number): Promise<ContainerDto> {
+    const axios = useAxiosInstance();
+    console.debug('find containers');
+    return new Promise<ContainerDto>((resolve, reject) => {
+      axios.get<ContainerDto>(`/api/container/${containerId}`)
+        .then((response) => {
+          console.info(`Find container with id ${containerId}`)
+          resolve(response.data)
+        })
+        .catch((error) => {
+          console.error('Failed to find container', error)
+          reject(axiosErrorToApiError(error))
+        })
+    })
+  }
+
+  return {findAll, findOne}
 }
diff --git a/dbrepo-ui/composables/database-service.ts b/dbrepo-ui/composables/database-service.ts
index b96d75ab80e880f52d416fef382f9d22de444502..7956f7b4dff6cb748733fd381275dd6d0622b60c 100644
--- a/dbrepo-ui/composables/database-service.ts
+++ b/dbrepo-ui/composables/database-service.ts
@@ -83,7 +83,7 @@ export const useDatabaseService = (): any => {
     });
   }
 
-  async function findOne(id: number): Promise<DatabaseDto | null> {
+  async function findOne(id: number, rawError: boolean = false): Promise<DatabaseDto | null> {
     const axios = useAxiosInstance();
     console.debug('find database with id', id);
     return new Promise((resolve, reject) => {
@@ -94,6 +94,9 @@ export const useDatabaseService = (): any => {
         })
         .catch((error) => {
           console.error('Failed to find database', error);
+          if (rawError) {
+            reject(error)
+          }
           reject(axiosErrorToApiError(error));
         });
     });
diff --git a/dbrepo-ui/composables/identifier-service.ts b/dbrepo-ui/composables/identifier-service.ts
index f85c48dc21f1428ea0656a00861eb260e48c0cf5..3853d9df751aef86b62e2cb30394cf0be1896efb 100644
--- a/dbrepo-ui/composables/identifier-service.ts
+++ b/dbrepo-ui/composables/identifier-service.ts
@@ -328,6 +328,9 @@ export const useIdentifierService = (): any => {
   }
 
   function databaseToServerHead(database: DatabaseDto) {
+    if (!database) {
+      return
+    }
     const config = useRuntimeConfig()
     /* Google Rich Results */
     const json: any = {
diff --git a/dbrepo-ui/composables/search-service.ts b/dbrepo-ui/composables/search-service.ts
index 62be8b9bc7160f70e33969970fbbba1c500d288f..b61f8358cf74167d1fef5c6f2de54ef017d4c7aa 100644
--- a/dbrepo-ui/composables/search-service.ts
+++ b/dbrepo-ui/composables/search-service.ts
@@ -18,11 +18,11 @@ export const useSearchService = (): any => {
     })
   }
 
-  async function fuzzy_search(term: string): Promise<SearchResultDto> {
+  async function fuzzy_search(term: string): Promise<DatabaseDto[]> {
     const axios = useAxiosInstance()
     console.debug('fuzzy search for term', term)
-    return new Promise<SearchResultDto>((resolve, reject) => {
-      axios.get<SearchResultDto>(`/api/search?q=${term}`)
+    return new Promise<DatabaseDto[]>((resolve, reject) => {
+      axios.get<DatabaseDto[]>(`/api/search?q=${term}`)
         .then((response) => {
           console.info('Searched for term', term)
           resolve(response.data)
diff --git a/dbrepo-ui/composables/table-service.ts b/dbrepo-ui/composables/table-service.ts
index f2e7a3a2f9f07cf9ed25b1220190e22879ef7a83..ca757c7451d70c4bd1fffc36b72c6a23d7fdde58 100644
--- a/dbrepo-ui/composables/table-service.ts
+++ b/dbrepo-ui/composables/table-service.ts
@@ -29,7 +29,7 @@ export const useTableService = (): any => {
           resolve(response.data)
         })
         .catch((error) => {
-          console.error('Failed to find table')
+          console.error('Failed to find table', error)
           reject(axiosErrorToApiError(error))
         })
     })
@@ -287,6 +287,7 @@ export const useTableService = (): any => {
     exportData,
     create,
     remove,
+    updateSemantics,
     removeTuple,
     history,
     suggest,
diff --git a/dbrepo-ui/composables/view-service.ts b/dbrepo-ui/composables/view-service.ts
index 4c948a57f10b5637cb441baa83e42bb9c3850a46..417f5a645e978cc8f93e7fc524df22756c5b093e 100644
--- a/dbrepo-ui/composables/view-service.ts
+++ b/dbrepo-ui/composables/view-service.ts
@@ -24,7 +24,7 @@ export const useViewService = (): any => {
     return new Promise<ViewDto>((resolve, reject) => {
       axios.get<ViewDto>(`/api/database/${databaseId}/view/${viewId}`)
         .then((response) => {
-          console.info('Deleted view with id', viewId, 'in database with id', databaseId)
+          console.info('Found view with id', viewId, 'in database with id', databaseId)
           resolve(response.data)
         })
         .catch((error) => {
diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue
index 8816ab5c9190662331e8ff86b85984cb92eec37f..ad00d8117309a83844d42d2c52faa44c34a55a0b 100644
--- a/dbrepo-ui/layouts/default.vue
+++ b/dbrepo-ui/layouts/default.vue
@@ -100,7 +100,7 @@
           class="mr-2"
           color="secondary"
           variant="flat"
-          prepend-icon="mdi-login"
+          :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-login' : null"
           to="/login">
           {{ $t('navigation.login') }}
         </v-btn>
@@ -108,7 +108,7 @@
           v-if="!user"
           color="primary"
           variant="flat"
-          prepend-icon="mdi-account-plus"
+          :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-account-plus' : null"
           to="/signup">
           {{ $t('navigation.signup') }}
         </v-btn>
@@ -150,32 +150,47 @@
     <v-main>
       <v-container>
         <slot />
+        <JumboBox
+          v-if="error"
+          :title="$t(errorCodeKey(error).title, { resource })"
+          :subtitle="$t(errorCodeKey(error).subtitle)"
+          :text="$t(errorCodeKey(error).text, { resource })" />
       </v-container>
     </v-main>
   </v-app>
 </template>
 
 <script setup>
-const config = useRuntimeConfig()
+import { ref } from 'vue'
+
+const runtimeConfig = useRuntimeConfig()
+const config = ref(runtimeConfig)
 useServerHead({
-  title: config.public.title,
+  title: runtimeConfig.public.title,
   meta: [
-    {'ref': 'icon', type: 'image/x-icon', href: config.public.icon},
-    {'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests'}
+    { 'ref': 'icon', type: 'image/x-icon', href: runtimeConfig.public.icon },
+    { 'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests' }
   ]
 })
 </script>
 <script>
+import JumboBox from '@/components/JumboBox.vue'
 import { useUserStore } from '@/stores/user'
 import { useCacheStore } from '@/stores/cache'
+import { errorCodeKey, makeError } from '@/utils'
 
 export default {
+  components: {
+    JumboBox
+  },
   data () {
     return {
       drawer: false,
       model: null,
       query: null,
       loading: true,
+      databaseError: null,
+      accessError: null,
       searchResults: [],
       databases: [],
       loadingUser: true,
@@ -199,12 +214,36 @@ export default {
     messages () {
       return this.cacheStore.getMessages
     },
+    access () {
+      return this.userStore.getAccess
+    },
     table () {
       return this.cacheStore.getTable
     },
+    view () {
+      return this.cacheStore.getView
+    },
+    subset () {
+      return this.cacheStore.getSubset
+    },
     database () {
       return this.cacheStore.getDatabase
     },
+    resource () {
+      if (!this.$route.params.database_id) {
+        return null
+      }
+      if (this.$route.params.table_id) {
+        return 'table'
+      }
+      if (this.$route.params.view_id) {
+        return 'view'
+      }
+      if (this.$route.params.subset_id) {
+        return 'subset'
+      }
+      return 'database'
+    },
     roles () {
       return this.userStore.getRoles
     },
@@ -220,6 +259,27 @@ export default {
     commitShort () {
       return this.$config.public.commit.substr(0, 8)
     },
+    error () {
+      if (this.databaseError) {
+        return this.databaseError
+      }
+      if (this.accessError) {
+        return this.accessError
+      }
+      if (!this.user) {
+        return null
+      }
+      if (this.table && !this.table.is_public && !this.table.is_schema_public && this.table.owner.id !== this.user.id) {
+        return makeError(403, null, null)
+      }
+      if (this.view && !this.view.is_public && !this.view.is_schema_public && this.view.owner.id !== this.user.id) {
+        return makeError(403, null, null)
+      }
+      if (this.subset && !this.subset.is_public && !this.subset.is_schema_public && this.subset.owner.id !== this.user.id) {
+        return makeError(403, null, null)
+      }
+      return null
+    },
     canListOntologies () {
       if (!this.roles) {
         return false
@@ -244,18 +304,42 @@ export default {
     '$route.params': {
       handler (newObj, oldObj) {
         if (!newObj.database_id) {
+          this.databaseError = null
+          this.accessError = null
+          this.cacheStore.setTable(null)
+          this.cacheStore.setView(null)
+          this.cacheStore.setSubset(null)
+          return
+        }
+        if (import.meta.server) {
           return
         }
         /* load database and optional access */
         this.cacheStore.setRouteDatabase(newObj.database_id)
+          .catch((error) => {
+            this.databaseError = error
+          })
         if (this.user) {
           this.userStore.setRouteAccess(newObj.database_id)
         }
-        if (!newObj.table_id) {
-          return
-        }
         /* load table */
-        this.cacheStore.setRouteTable(newObj.database_id, newObj.table_id)
+        if (newObj.table_id) {
+          this.cacheStore.setRouteTable(newObj.database_id, newObj.table_id)
+        } else {
+          this.cacheStore.setTable(null)
+        }
+        /* load view */
+        if (newObj.view_id) {
+          this.cacheStore.setRouteView(newObj.database_id, newObj.view_id)
+        } else {
+          this.cacheStore.setView(null)
+        }
+        /* load subset */
+        if (newObj.subset_id) {
+          this.cacheStore.setRouteSubset(newObj.database_id, newObj.subset_id)
+        } else {
+          this.cacheStore.setSubset(null)
+        }
       },
       deep: true,
       immediate: true
@@ -273,6 +357,7 @@ export default {
     this.cacheStore.reloadMessages()
   },
   methods: {
+    errorCodeKey,
     login () {
       const redirect = ![undefined, '/', '/login'].includes(this.$router.currentRoute.path)
       this.$router.push({ path: '/login', query: redirect ? { redirect: this.$router.currentRoute.path } : {} })
diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json
index 655ec2a345b98294cf6512c531f83a67c9196d3c..2e94f572ec68f7f3c595254ed5c9a5e14099e623 100644
--- a/dbrepo-ui/locales/en-US.json
+++ b/dbrepo-ui/locales/en-US.json
@@ -37,7 +37,8 @@
     "modify": "Modify",
     "help": "Help",
     "visibility": "Visibility",
-    "update": "Update"
+    "update": "Update",
+    "you": "You"
   },
   "pages": {
     "identifier": {
@@ -291,12 +292,13 @@
           "read": "You can read all contents of this table"
         }
       },
-      "visibility": {
-        "title": "Visibility",
-        "open": "Open",
-        "data": "Data is public",
-        "schema": "Schema is public",
-        "closed": "Closed"
+      "settings": {
+        "title": "Metadata",
+        "subtitle": "Optional table description for humans and visibility settings"
+      },
+      "delete": {
+        "title": "Delete this table",
+        "subtitle": "This action deletes {table} and all data in it, there is no going back"
       },
       "description": {
         "title": "Description",
@@ -599,16 +601,30 @@
       "internal-name": {
         "title": "Internal Name"
       },
-      "visibility": {
-        "title": "Visibility",
-        "open": "Open",
-        "data": "Data is public",
-        "schema": "Schema is public",
-        "closed": "Closed"
-      },
       "size": {
         "title": "Size"
       },
+      "status": {
+        "title": "Status",
+        "public": "Public",
+        "data": "Data-only",
+        "schema": "Schema-only",
+        "draft": "Draft"
+      },
+      "resource": {
+        "data": {
+          "label": "Transparency",
+          "hint": "Required, e.g. can hide the {resource} from lists, search, etc.",
+          "enabled": "Visible",
+          "disabled": "Hidden"
+        },
+        "schema": {
+          "label": "Insights",
+          "hint": "Required, e.g. can hide insights on the {resource} such as {schema}.",
+          "enabled": "Visible",
+          "disabled": "Hidden"
+        }
+      },
       "owner": {
         "title": "Owner"
       },
@@ -625,7 +641,7 @@
       },
       "subpages": {
         "access": {
-          "title": "Database Access",
+          "title": "Access to database",
           "subtitle": "Overview on users with their access to the database",
           "read": "Read all contents",
           "write-own": "Read all contents & write own tables",
@@ -649,22 +665,14 @@
             "hint": "Required. The internal database name will be lowercase alphanumeric, others will be replaced with _",
             "placeholder": "e.g. my_database, air_quality"
           },
+          "draft": {
+            "label": "Draft",
+            "hint": "Hides the database, only users with access can see it"
+          },
           "engine": {
             "label": "Engine",
             "hint": "Required"
           },
-          "visibility": {
-            "label": "Visibility",
-            "hint": "Required",
-            "public": {
-              "label": "Public",
-              "hint": "Everything is visible to the public, e.g. to publish data used in a open-access paper. You can later update the visibility of data."
-            },
-            "private": {
-              "label": "Private",
-              "hint": "Nothing is visible to anyone without dedicated read-access, e.g. to work on a dataset. You can later update the visibility of data."
-            }
-          },
           "submit": {
             "text": "Create"
           },
@@ -727,11 +735,6 @@
               "hint": "Required",
               "help": "Public = visible to anyone, Private = visible only to designated users"
             },
-            "schema": {
-              "label": "Schema Visibility",
-              "hint": "Required",
-              "help": "Public = visible to anyone, Private = visible only to designated users"
-            },
             "submit": {
               "text": "Modify"
             }
@@ -917,6 +920,14 @@
       "visibility": {
         "title": "Visibility"
       },
+      "delete": {
+        "title": "Delete this view",
+        "subtitle": "This action deletes {view}, there is no going back"
+      },
+      "settings": {
+        "title": "Metadata",
+        "subtitle": "Visibility settings"
+      },
       "subpages": {
         "create": {
           "title": "Create View",
@@ -1088,6 +1099,18 @@
     }
   },
   "error": {
+    "permission": {
+      "title": "You do not have permission to view this {resource}",
+      "text": "This a default-fallback error message since this {resource} marked as private. Please try logging in or request read-access permissions from the owner."
+    },
+    "missing": {
+      "title": "The requested {resource} was not found",
+      "text": "We could not find the requested {resource} anywhere."
+    },
+    "gone": {
+      "title": "The requested {resource} does not exist anymore",
+      "text": "We could not find the requested {resource} anymore."
+    },
     "auth": {
       "connection": "Failed to contact auth service",
       "invalid": "Failed to authenticate in auth service"
@@ -1120,7 +1143,7 @@
     },
     "database": {
       "connection": "Failed to contact database",
-      "create":  "Failed to create database in data service",
+      "create": "Failed to create database in data service",
       "invalid": "Failed to perform action in database",
       "querystore": "Failed to insert query into query store",
       "missing": "Failed to find database in metadata database"
@@ -1273,18 +1296,18 @@
       "dataset": "Successfully analysed dataset"
     },
     "access": {
-      "created": "Successfully provisioned access",
-      "modified": "Successfully modified access",
-      "revoked": "Successfully revoked access"
+      "created": "Granted {access} access successfully",
+      "modified": "Updated {access} access successfully",
+      "revoked": "Revoked {access} access successfully"
     },
     "data": {
       "add": "Successfully added data entry",
       "update": "Successfully updated data entry"
     },
     "table": {
-      "created": "Successfully created table",
+      "created": "Created table {table} successfully",
       "semantics": "Successfully assigned semantic instance",
-      "updated": "Successfully updated table"
+      "updated": "Updated table {table} successfully"
     },
     "schema": {
       "tables": "Successfully refreshed database tables metadata.",
@@ -1310,7 +1333,7 @@
       "info": "Successfully updated user information",
       "theme": "Successfully updated user theme",
       "password": "Successfully updated user password",
-      "login": "Successfully logged in"
+      "login": "Welcome back, {username}!"
     },
     "view": {
       "create": "Successfully created view",
@@ -1345,8 +1368,6 @@
     "database": {
       "recent": "Recent Databases",
       "links": "Important Links",
-      "public": "Public",
-      "private": "Private",
       "current": "Current Data",
       "history": "Historic Data",
       "create": {
diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts
index cc68f1bd909e30f56b9081c4b869ada488e38e86..4bce6ec5c5b3dc7f025734d9bf15d8e914123f79 100644
--- a/dbrepo-ui/nuxt.config.ts
+++ b/dbrepo-ui/nuxt.config.ts
@@ -20,8 +20,6 @@ if (process.env.NODE_ENV === 'development') {
 /**
  * https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering
  */
-const routeRules = {}
-
 export default defineNuxtConfig({
   app: {
     head: {
@@ -105,8 +103,6 @@ export default defineNuxtConfig({
     }
   },
 
-  routeRules,
-
   devServer: {
     port: 3001
   },
diff --git a/dbrepo-ui/package.json b/dbrepo-ui/package.json
index 856f56f4f0294cbc555f7d8d0a3d2e931d45ea58..a1354778200ebabb28a575e204e264e6bd155774 100644
--- a/dbrepo-ui/package.json
+++ b/dbrepo-ui/package.json
@@ -23,6 +23,7 @@
     "buffer": "^6.0.3",
     "chart.js": "^4.4.1",
     "date-fns": "^3.3.1",
+    "http-status-codes": "^2.3.0",
     "jwt-decode": "^4.0.0",
     "merkle-json": "^2.6.0",
     "moment": "^2.30.1",
diff --git a/dbrepo-ui/pages/database/[database_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/info.vue
index 5b739987fdcaa422d31e0fc2545bcdeacbdf38ad..02c2c8310ae9bafe8ce458decf6ccdd86a6cba37 100644
--- a/dbrepo-ui/pages/database/[database_id]/info.vue
+++ b/dbrepo-ui/pages/database/[database_id]/info.vue
@@ -1,5 +1,6 @@
 <template>
-  <div>
+  <div
+    v-if="canViewSchema">
     <DatabaseToolbar />
     <v-window
       v-model="tab">
@@ -58,26 +59,13 @@
                 </div>
               </v-list-item>
               <v-list-item
-                :title="$t('pages.database.visibility.title')"
-                density="compact">
-                {{ databaseVisibility }}
-              </v-list-item>
-              <v-list-item
+                v-if="databaseSize"
                 :title="$t('pages.database.size.title')"
                 density="compact">
                 <div>
                   {{ databaseSize }}
                 </div>
               </v-list-item>
-              <v-list-item
-                :title="$t('pages.database.owner.title')"
-                density="compact">
-                <div>
-                  <UserBadge
-                    :user="database.owner"
-                    :other-user="user" />
-                </div>
-              </v-list-item>
               <v-list-item
                 v-if="access && access.type"
                 :title="$t('pages.database.subpages.access.title')"
@@ -101,11 +89,13 @@
                 </div>
               </v-list-item>
               <v-list-item
-                v-if="access"
-                :title="$t('pages.database.connection.title')"
+                :title="$t('pages.database.owner.title')"
                 density="compact">
-                <pre
-                  class="pb-1">{{ jdbcString }}</pre>
+                <div>
+                  <UserBadge
+                    :user="database.owner"
+                    :other-user="user" />
+                </div>
               </v-list-item>
               <v-list-item
                 v-if="database.contact"
@@ -173,22 +163,12 @@
   </div>
 </template>
 
-<script setup>
-const config = useRuntimeConfig()
-const { database_id } = useRoute().params
-const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}`)
-if (data.value) {
-  const identifierService = useIdentifierService()
-  useServerHead(identifierService.databaseToServerHead(data.value))
-  useServerSeoMeta(identifierService.databaseToServerSeoMeta(data.value))
-}
-</script>
 <script>
 import DatabaseToolbar from '@/components/database/DatabaseToolbar.vue'
 import Summary from '@/components/identifier/Summary.vue'
 import Select from '@/components/identifier/Select.vue'
 import UserBadge from '@/components/user/UserBadge.vue'
-import { formatTimestampUTCLabel, sizeToHumanLabel } from '@/utils'
+import { sizeToHumanLabel } from '@/utils'
 import { useUserStore } from '@/stores/user'
 import { useCacheStore } from '@/stores/cache'
 
@@ -201,6 +181,7 @@ export default {
   },
   data () {
     return {
+      error: null,
       items: [
         {
           title: this.$t('navigation.databases'),
@@ -245,6 +226,9 @@ export default {
     user () {
       return this.userStore.getUser
     },
+    database () {
+      return this.cacheStore.getDatabase
+    },
     roles () {
       return this.userStore.getRoles
     },
@@ -261,7 +245,7 @@ export default {
       if (!this.user) {
         return this.identifiers.filter(i => i.status === 'published')
       }
-      return this.identifiers.filter(i => i.status === 'published' || i.creator.id === this.user.id)
+      return this.identifiers.filter(i => i.status === 'published' || i.owner.id === this.user.id)
     },
     identifier () {
       if (this.pid) {
@@ -275,9 +259,6 @@ export default {
     access () {
       return this.userStore.getAccess
     },
-    database () {
-      return this.cacheStore.getDatabase
-    },
     pid () {
       return this.$route.query.pid
     },
@@ -337,13 +318,6 @@ export default {
           return { text: null, class: null }
       }
     },
-    jdbcString () {
-      if (!this.database || !this.user) {
-        return
-      }
-      const flags = this.database.container.ui_additional_flags ? this.database.container.ui_additional_flags : ''
-      return `jdbc:${this.database.container.image.jdbc_method}://${this.database.container.ui_host}:${this.database.container.ui_port}/${this.database.internal_name}${flags} (${this.$t('pages.database.connection.username')}=${this.user.username}, ${this.$t('pages.database.connection.password')}=yourpassword)`
-    },
     databaseExtraInfo () {
       return this.$config.public.database.extra
     },
@@ -355,23 +329,17 @@ export default {
       this.database.tables.forEach((t) => { sum += t.data_length })
       return sizeToHumanLabel(sum)
     },
-    databaseVisibility () {
-      if (!this.database) {
-        return null
-      }
-      if (this.database.is_public && this.database.is_schema_public) {
-        return this.$t('pages.database.visibility.open')
-      }
-      if (!this.database.is_public && !this.database.is_schema_public) {
-        return this.$t('pages.database.visibility.closed')
-      }
-      return this.database.is_public ? this.$t('pages.database.visibility.data') : this.$t('pages.database.visibility.schema')
-    },
     previewImage () {
       if (!this.database) {
         return null
       }
       return this.database.preview_image
+    },
+    canViewSchema () {
+      if (this.error) {
+        return false
+      }
+      return this.database
     }
   }
 }
diff --git a/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue
index 7de1348fdce246da3f47d8869f9705d13d5f7c7d..a57b439b216ae4743fd4bda292dc53e5f7e05a69 100644
--- a/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/persist/[identifier_id]/index.vue
@@ -1,6 +1,9 @@
 <template>
-  <div v-if="canCreateIdentifier || canUpdateIdentifier">
-    <Persist type="database" :database="database" />
+  <div
+    v-if="canCreateIdentifier || canUpdateIdentifier">
+    <Persist
+      type="database"
+      :database="database" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
diff --git a/dbrepo-ui/pages/database/[database_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/persist/index.vue
index 0981a547905ad08494c980c3d07dce2242c4ea04..df675d262e8e116b0c4252d74fe6878681388acb 100644
--- a/dbrepo-ui/pages/database/[database_id]/persist/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/persist/index.vue
@@ -1,6 +1,9 @@
 <template>
-  <div v-if="canCreateIdentifier || canUpdateIdentifier">
-    <Persist type="database" :database="database" />
+  <div
+    v-if="canCreateIdentifier || canUpdateIdentifier">
+    <Persist
+      type="database"
+      :database="database" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
diff --git a/dbrepo-ui/pages/database/[database_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/settings.vue
index b3d5b634613c358b01bc13d53f270fe7ef7e4d2a..aed13a4e1e3ca25db85b8f905415a55336ba9fa2 100644
--- a/dbrepo-ui/pages/database/[database_id]/settings.vue
+++ b/dbrepo-ui/pages/database/[database_id]/settings.vue
@@ -1,5 +1,6 @@
 <template>
-  <div>
+  <div
+    v-if="canView">
     <DatabaseToolbar
       ref="toolbar" />
     <v-window
@@ -132,56 +133,35 @@
           :title="$t('pages.database.subpages.settings.visibility.title')"
           :subtitle="$t('pages.database.subpages.settings.visibility.subtitle')">
           <v-card-text>
-            <v-row>
-              <v-col md="8">
+            <v-row
+              dense>
+              <v-col
+                md="4">
                 <v-select
                   v-model="modifyVisibility.is_public"
-                  :items="visibility"
-                  :variant="inputVariant"
-                  :label="$t('pages.database.subpages.settings.visibility.data.label')"
-                  :hint="$t('pages.database.subpages.settings.visibility.data.hint')"
+                  :items="dataOptions"
                   persistent-hint
-                  name="visibility">
-                  <template
-                    v-slot:append>
-                    <v-tooltip
-                      location="bottom">
-                      <template
-                        v-slot:activator="{ props }">
-                        <v-icon
-                          v-bind="props"
-                          icon="mdi-help-circle-outline" />
-                      </template>
-                      {{ $t('pages.database.subpages.settings.visibility.data.help') }}
-                    </v-tooltip>
-                  </template>
-                </v-select>
+                  :variant="inputVariant"
+                  required
+                  :rules="[
+                    v => v !== null || $t('validation.required')
+                  ]"
+                  :label="$t('pages.database.resource.data.label')"
+                  :hint="$t('pages.database.resource.data.hint', { resource: 'database' })" />
               </v-col>
-            </v-row>
-            <v-row>
-              <v-col md="8">
+              <v-col
+                md="4">
                 <v-select
                   v-model="modifyVisibility.is_schema_public"
-                  :items="visibility"
-                  :variant="inputVariant"
-                  :label="$t('pages.database.subpages.settings.visibility.schema.label')"
-                  :hint="$t('pages.database.subpages.settings.visibility.schema.hint')"
+                  :items="schemaOptions"
                   persistent-hint
-                  name="schema-visibility">
-                  <template
-                    v-slot:append>
-                    <v-tooltip
-                      location="bottom">
-                      <template
-                        v-slot:activator="{ props }">
-                        <v-icon
-                          v-bind="props"
-                          icon="mdi-help-circle-outline" />
-                      </template>
-                      {{ $t('pages.database.subpages.settings.visibility.schema.help') }}
-                    </v-tooltip>
-                  </template>
-                </v-select>
+                  :variant="inputVariant"
+                  required
+                  :rules="[
+                    v => v !== null || $t('validation.required')
+                  ]"
+                  :label="$t('pages.database.resource.schema.label')"
+                  :hint="$t('pages.database.resource.schema.hint', { resource: 'database', schema: 'tables, views, subsets' })" />
               </v-col>
             </v-row>
             <v-row>
@@ -302,15 +282,13 @@ export default {
       modifyImage: {
         key: null
       },
-      visibility: [
-        {
-          title: this.$t('toolbars.database.public'),
-          value: true
-        },
-        {
-          title: this.$t('toolbars.database.private'),
-          value: false
-        }
+      dataOptions: [
+        { title: this.$t('pages.database.resource.data.enabled'), value: true },
+        { title: this.$t('pages.database.resource.data.disabled'), value: false },
+      ],
+      schemaOptions: [
+        { title: this.$t('pages.database.resource.schema.enabled'), value: true },
+        { title: this.$t('pages.database.resource.schema.disabled'), value: false },
       ],
       headers: [
         {
@@ -428,6 +406,12 @@ export default {
       }
       return this.roles.includes('modify-database-image')
     },
+    canView () {
+      if (this.error) {
+        return false
+      }
+      return this.database
+    },
     previewImage () {
       if (this.file) {
         return URL.createObjectURL(this.file)
@@ -541,10 +525,13 @@ export default {
           this.modifyImage.key = null
           this.loadingImage = false
         })
-        .catch(() => {
-          const toast = useToastInstance()
-          toast.error('Failed to modify image')
+        .catch(({code}) => {
           this.loadingImage = false
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(this.$t(code))
         })
         .finally(() => {
           this.loadingImage = false
diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue
index 3ac8f40d645b44449f5607b9f3b031d06fa463d6..37c34a71677e1e8aa92464d43c47d89a95d22a81 100644
--- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue
+++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue
@@ -33,7 +33,9 @@
         :loading="loadingSubset"
         @click="loadSubset" />
     </v-toolbar>
-    <v-card tile>
+    <v-card
+      v-if="subset"
+      tile>
       <QueryResults
         id="query-results"
         ref="queryResults"
@@ -84,9 +86,6 @@ export default {
           disabled: true
         }
       ],
-      subset: {
-        id: this.$route.params.subset_id
-      },
       cacheStore: useCacheStore()
     }
   },
@@ -94,6 +93,9 @@ export default {
     database () {
       return this.cacheStore.getDatabase
     },
+    subset () {
+      return this.cacheStore.getSubset
+    },
     executionUTC () {
       if (!this.subset) {
         return null
@@ -113,11 +115,11 @@ export default {
       if (this.database.is_public) {
         return true
       }
-      return this.subset.creator.username === this.username
+      return this.subset.owner.username === this.username
     },
   },
   mounted () {
-    this.loadSubset()
+    this.loadResult()
   },
   methods: {
     loadSubset () {
@@ -136,8 +138,10 @@ export default {
         })
     },
     loadResult () {
-      this.$refs.queryResults.reExecute(this.subset.id)
-      this.$refs.queryResults.reExecuteCount(this.subset.id)
+      if (this.subset) {
+        this.$refs.queryResults.reExecute(this.subset.id)
+        this.$refs.queryResults.reExecuteCount(this.subset.id)
+      }
     },
     download () {
       this.downloadLoading = true
@@ -155,6 +159,9 @@ export default {
         .catch(({code}) => {
           this.downloadLoading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
         })
         .finally(() => {
@@ -164,5 +171,3 @@ export default {
   }
 }
 </script>
-<style>
-</style>
diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue
index 764d1b55ffa6b134c07e86290325525caa8919b2..f27153bebfc710b82f25fb0cc3d3a92297472aac 100644
--- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue
+++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue
@@ -22,7 +22,7 @@
       :title="$t('pages.subset.title')">
       <v-card-text>
         <v-list
-          v-if="loadingSubset && !subset"
+          v-if="!subset"
           lines="two"
           dense>
           <v-skeleton-loader
@@ -37,7 +37,14 @@
             v-if="database"
             :title="$t('pages.subset.visibility.title')"
             density="compact">
-            {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }}
+            <ResourceStatus
+              v-if="!identifier"
+              :inline="true"
+              :resource="database" />
+            <ResourceStatus
+              v-else
+              :inline="true"
+              :resource="identifier" />
           </v-list-item>
           <v-list-item
             v-if="subset.creator"
@@ -64,7 +71,8 @@
           <v-list-item
             :title="`${$t('pages.subset.result.title')} ${$t('pages.subset.hash.title')}`"
             density="compact">
-            <pre>{{ $t('pages.subset.hash.prefix') }}:{{ result_hash }}</pre>
+            <pre v-if="subset.result_hash">{{ $t('pages.subset.hash.prefix') }}:{{ subset.result_hash }}</pre>
+            <span v-else>(none)</span>
           </v-list-item>
           <v-list-item
             :title="$t('pages.subset.rows.title')"
@@ -74,49 +82,10 @@
         </v-list>
       </v-card-text>
     </v-card>
-    <v-divider />
-    <v-card
-      :title="$t('pages.database.title')"
-      variant="flat"
-      rounded="0">
-      <v-card-text>
-        <v-list
-          v-if="database"
-          dense>
-          <v-list-item
-            :title="$t('pages.database.visibility.title')">
-            {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }}
-          </v-list-item>
-          <v-list-item
-            :title="$t('pages.database.name.title')">
-            <NuxtLink
-              class="text-primary"
-              :to="`/database/${database.id}`">
-              {{ database.internal_name }}
-            </NuxtLink>
-          </v-list-item>
-        </v-list>
-      </v-card-text>
-    </v-card>
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
 
-<script setup>
-const config = useRuntimeConfig()
-const { database_id, subset_id } = useRoute().params
-const requestConfig = { timeout: 90_000, headers: { Accept: 'application/json', 'Content-Type': 'application/json' } }
-const userStore = useUserStore()
-if (userStore.getToken) {
-  requestConfig.headers.Authorization = `Bearer ${userStore.getToken}`
-}
-const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}/subset/${subset_id}`, requestConfig)
-if (data.value) {
-  const identifierService = useIdentifierService()
-  useServerHead(identifierService.subsetToServerHead(data.value))
-  useServerSeoMeta(identifierService.subsetToServerSeoMeta(data.value))
-}
-</script>
 <script>
 import Summary from '@/components/identifier/Summary.vue'
 import SubsetToolbar from '@/components/subset/SubsetToolbar.vue'
@@ -162,11 +131,9 @@ export default {
       persistQueryDialog: false,
       loadingDatabase: false,
       loadingIdentifier: false,
-      loadingSubset: true,
       downloadLoading: false,
       error: false,
       promises: [],
-      subset: null,
       userStore: useUserStore(),
       cacheStore: useCacheStore()
     }
@@ -181,6 +148,9 @@ export default {
     access () {
       return this.userStore.getAccess
     },
+    subset () {
+      return this.cacheStore.getSubset
+    },
     user () {
       return this.userStore.getUser
     },
@@ -212,12 +182,6 @@ export default {
       }
       return enTitle[0].title
     },
-    result_hash () {
-      if (!this.subset.result_hash) {
-        return '(none)'
-      }
-      return this.subset.result_hash
-    },
     publisher () {
       if (this.database.publisher === null) {
         return 'NA'
@@ -230,25 +194,6 @@ export default {
       }
       return formatTimestampUTCLabel(this.subset.created)
     }
-  },
-  mounted () {
-    this.loadSubset()
-  },
-  methods: {
-    loadSubset () {
-      this.loadingSubset = true
-      const queryService = useQueryService()
-      queryService.findOne(this.$route.params.database_id, this.$route.params.subset_id)
-        .then((subset) => {
-          this.subset = subset
-        })
-        .catch(() => {
-          this.loadingSubset = false
-        })
-        .finally(() => {
-          this.loadingSubset = false
-        })
-    }
   }
 }
 </script>
diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue
index a3b7306643fbc455475e882b41b86ae1744ae013..b15ecb3292520979e550f68c7c16ef873638b124 100644
--- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/[identifier_id]/index.vue
@@ -1,6 +1,9 @@
 <template>
-  <div v-if="canCreateIdentifier || canUpdateIdentifier">
-    <Persist type="subset" :database="database" />
+  <div
+    v-if="canCreateIdentifier || canUpdateIdentifier">
+    <Persist
+      type="subset"
+      :database="database" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue
index 01ba88a36c32142f68dbdeb94da99c86cdf10197..07be66bc73911c9bd7580991ef2c71c6d36de5bc 100644
--- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/persist/index.vue
@@ -1,6 +1,10 @@
 <template>
-  <div v-if="canPersistQuery">
-    <Persist type="subset" :database="database" :query="query" />
+  <div
+    v-if="canPersistQuery">
+    <Persist
+      type="subset"
+      :database="database"
+      :query="query" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
diff --git a/dbrepo-ui/pages/database/[database_id]/subset/create.vue b/dbrepo-ui/pages/database/[database_id]/subset/create.vue
index 5202bc633626e135e263056fa39b5022676c1575..b533dfb1a8e0af06cd1428032ebf188c9d3c4b0f 100644
--- a/dbrepo-ui/pages/database/[database_id]/subset/create.vue
+++ b/dbrepo-ui/pages/database/[database_id]/subset/create.vue
@@ -1,5 +1,6 @@
 <template>
-  <div v-if="canCreateSubset">
+  <div
+    v-if="canCreateSubset">
     <Builder />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
diff --git a/dbrepo-ui/pages/database/[database_id]/subset/index.vue b/dbrepo-ui/pages/database/[database_id]/subset/index.vue
index 8d454ce83d11a4f6143093ecb20d268b1897a9d2..d031b3f3f654f260099e109cc17f5c61cf0c4eb6 100644
--- a/dbrepo-ui/pages/database/[database_id]/subset/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/subset/index.vue
@@ -1,5 +1,6 @@
 <template>
-  <div>
+  <div
+    v-if="canViewSchema">
     <DatabaseToolbar />
     <SubsetList />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
@@ -8,6 +9,7 @@
 
 <script>
 import SubsetList from '@/components/subset/SubsetList.vue'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   components: {
@@ -29,7 +31,19 @@ export default {
           to: `/database/${this.$route.params.database_id}/subset`,
           disabled: true
         }
-      ]
+      ],
+      cacheStore: useCacheStore()
+    }
+  },
+  computed: {
+    database () {
+      return this.cacheStore.getDatabase
+    },
+    canViewSchema () {
+      if (this.error) {
+        return false
+      }
+      return this.database
     }
   }
 }
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue
index cac7b4ab3dfca87c882b5032d9ed87dbd8ee356b..46e7f564a77cb4592c4df36c0bf07f5ce66f8ac1 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue
@@ -9,14 +9,14 @@
       <v-spacer />
       <v-btn
         v-if="canAddTuple"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-plus' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-plus' : null"
         variant="flat"
         :text="$t('toolbars.table.data.add')"
         class="ml-2"
         @click="addTuple" />
       <v-btn
         v-if="canEditTuple"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-pencil' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-pencil' : null"
         color="warning"
         variant="flat"
         :text="$t('toolbars.table.data.edit')"
@@ -24,7 +24,7 @@
         @click="editTuple" />
       <v-btn
         v-if="canDeleteTuple"
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-delete' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-delete' : null"
         color="error"
         variant="flat"
         :text="$t('toolbars.table.data.delete')"
@@ -32,14 +32,14 @@
         :loading="loadingDelete"
         @click="deleteItems" />
       <v-btn
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-download' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-download' : null"
         variant="flat"
         :loading="downloadLoading"
         :text="$t('toolbars.table.data.download')"
         class="ml-2"
         @click.stop="download" />
       <v-btn
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-refresh' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-refresh' : null"
         variant="flat"
         :text="$t('toolbars.table.data.refresh')"
         class="ml-2"
@@ -47,7 +47,7 @@
         :loading="loadingData"
         @click="reload" />
       <v-btn
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-update' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-update' : null"
         variant="flat"
         :text="$t('toolbars.table.data.version')"
         class="ml-2 mr-2"
@@ -61,13 +61,16 @@
         {{ $t('error.table.connection') }}
       </v-card-text>
     </v-card>
-    <v-card tile>
+    <v-card
+      tile>
       <QueryResults
         id="query-results"
         ref="queryResults"
+        class="mt-0 mb-0"
         type="table"
+        :select="canSelectTuples"
         :timestamp="versionISO || lastReload.toISOString()"
-        class="mt-0 mb-0" />
+        @selection="updateSelect" />
     </v-card>
     <v-dialog
       v-model="pickVersionDialog"
@@ -144,7 +147,6 @@ export default {
       version: null,
       lastReload: new Date(),
       tab: null,
-      error: false,
       tuple: null,
       options: {
         page: 1,
@@ -193,9 +195,6 @@ export default {
     user () {
       return this.userStore.getUser
     },
-    tables () {
-      return this.cacheStore.getTable
-    },
     access () {
       return this.userStore.getAccess
     },
@@ -238,8 +237,16 @@ export default {
       }
       return this.access.type === 'write_all'
     },
+    primaryKeyColumns () {
+      if (!this.table) {
+        return []
+      }
+      return this.table.constraints.primary_key.map(pk => pk.column)
+    },
     canViewTableData () {
-      /* view when database is public or when private: 1) view-table-data role present 2) access is at least read */
+      if (this.error) {
+        return false
+      }
       if (!this.table) {
         return false
       }
@@ -258,6 +265,13 @@ export default {
       const userService = useUserService()
       return userService.hasWriteAccess(this.table, this.access, this.user) && this.roles.includes('insert-table-data')
     },
+    canSelectTuples () {
+      if (!this.roles) {
+        return false
+      }
+      const userService = useUserService()
+      return userService.hasWriteAccess(this.table, this.access, this.user) && this.roles.includes('insert-table-data')
+    },
     canEditTuple () {
       if (!this.roles || this.selection === null || this.selection.length !== 1) {
         return false
@@ -299,8 +313,7 @@ export default {
       for (const select of this.selection) {
         /* remove in container */
         const constraints = {}
-        this.columns
-          .filter(c => c.is_primary_key)
+        this.primaryKeyColumns
           .forEach((c) => {
             constraints[c.internal_name] = select[c.internal_name]
           })
@@ -313,9 +326,12 @@ export default {
         }
         const tupleService = useTupleService()
         wait.push(tupleService.remove(this.$route.params.database_id, this.$route.params.table_id, { keys: constraints })
-          .catch(({message}) => {
+          .catch(({code, message}) => {
             const toast = useToastInstance()
-            toast.error(message)
+            if (typeof code !== 'string') {
+              return
+            }
+            toast.error(this.$t(code))
           }))
       }
       Promise.all(wait)
@@ -324,6 +340,7 @@ export default {
           toast.success(`Deleted ${this.selection.length} row(s)`)
           this.$emit('modified', { success: true, action: 'delete' })
           this.selection = []
+          this.$refs.queryResults.resetSelection()
           this.reload()
         })
       this.loadingDelete = false
@@ -345,6 +362,9 @@ export default {
           .catch(({code}) => {
             this.downloadLoading = false
             const toast = useToastInstance()
+            if (typeof code !== 'string') {
+              return
+            }
             toast.error(this.$t(code))
           })
           .finally(() => {
@@ -364,6 +384,9 @@ export default {
           .catch(({code}) => {
             this.downloadLoading = false
             const toast = useToastInstance()
+            if (typeof code !== 'string') {
+              return
+            }
             toast.error(this.$t(code))
           })
           .finally(() => {
@@ -390,11 +413,14 @@ export default {
     },
     reload () {
       this.lastReload = new Date()
+      if (!this.canViewTableData) {
+        return
+      }
       this.$refs.queryResults.reExecute(Number(this.$route.params.table_id))
       this.$refs.queryResults.reExecuteCount(Number(this.$route.params.table_id))
     },
     isFileField (column) {
-      return ['blob', 'longblob', 'mediumblob', 'tinyblob'].includes(column.column_type)
+      return ['blob', 'longblob', 'mediumblob', 'tinyblob'].includes(column.type)
     },
     close ({ success }) {
       console.debug('closed edit/create tuple dialog')
@@ -403,7 +429,11 @@ export default {
       if (success) {
         this.reload()
         this.selection = []
+        this.$refs.queryResults.resetSelection()
       }
+    },
+    updateSelect (selection) {
+      this.selection = selection
     }
   }
 }
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue
index 5f322fa498b4b05607db6422cc7964d98515cb74..8108e0677e040b5ce500b188e64857398a9c3cb6 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue
@@ -1,5 +1,6 @@
 <template>
-  <div>
+  <div
+    v-if="canViewSchema">
     <TableToolbar
       :selection="selection" />
     <v-card
@@ -21,45 +22,24 @@
       rounded="0"
       :title="$t('pages.table.title')">
       <v-card-text>
-        <v-skeleton-loader
-          v-if="!cachedTable"
-          type="list-item-three-line"
-          width="50%" />
         <v-list
-          v-if="cachedTable"
           dense>
-          <v-list-item
-            :title="$t('pages.table.id.title')">
-            {{ cachedTable.id }}
-          </v-list-item>
           <v-list-item
             :title="$t('pages.table.name.title')">
-            {{ cachedTable.internal_name }}
-          </v-list-item>
-          <v-list-item
-            :title="$t('pages.table.visibility.title')">
-            {{ databaseVisibility }}
+            {{ table.internal_name }}
           </v-list-item>
           <v-list-item
-            v-if="table"
             :title="$t('pages.table.size.title')">
             {{ sizeToHumanLabel(table.data_length) }}
           </v-list-item>
           <v-list-item
-            v-if="table"
+            v-if="canRead && table.num_rows"
             :title="$t('pages.table.rows.title')">
             {{ table.num_rows }}
           </v-list-item>
           <v-list-item
             :title="$t('pages.table.description.title')">
-            {{ hasDescription ? cachedTable.description : $t('pages.table.description.empty') }}
-          </v-list-item>
-          <v-list-item
-            :title="$t('pages.table.owner.title')">
-            <UserBadge
-              v-if="table"
-              :user="table.owner"
-              :other-user="user" />
+            {{ hasDescription ? table.description : $t('pages.table.description.empty') }}
           </v-list-item>
           <v-list-item
             v-if="accessDescription"
@@ -80,6 +60,12 @@
               </span>
             </span>
           </v-list-item>
+          <v-list-item
+            :title="$t('pages.table.owner.title')">
+            <UserBadge
+              :user="table.owner"
+              :other-user="user" />
+          </v-list-item>
         </v-list>
       </v-card-text>
     </v-card>
@@ -128,49 +114,15 @@
         </v-list>
       </v-card-text>
     </v-card>
-    <v-divider />
-    <v-card
-      :title="$t('pages.database.title')"
-      variant="flat">
-      <v-card-text>
-        <v-list dense>
-          <v-list-item
-            v-if="database"
-            :title="$t('pages.database.visibility.title')">
-            {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }}
-          </v-list-item>
-          <v-list-item
-            v-if="database"
-            :title="$t('pages.database.name.title')">
-            <NuxtLink
-              class="text-primary"
-              :to="`/database/${database.id}`">
-              {{ database.internal_name }}
-            </NuxtLink>
-          </v-list-item>
-        </v-list>
-      </v-card-text>
-    </v-card>
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
 
-<script setup>
-const config = useRuntimeConfig()
-const { database_id, table_id } = useRoute().params
-const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}/table/${table_id}`)
-if (data.value) {
-  const identifierService = useIdentifierService()
-  useServerHead(identifierService.tableToServerHead(data.value))
-  useServerSeoMeta(identifierService.tableToServerSeoMeta(data.value))
-}
-</script>
 <script>
 import TableToolbar from '@/components/table/TableToolbar.vue'
 import Select from '@/components/identifier/Select.vue'
 import Summary from '@/components/identifier/Summary.vue'
 import UserBadge from '@/components/user/UserBadge.vue'
-import { formatTimestampUTCLabel, sizeToHumanLabel } from '@/utils'
 import { useUserStore } from '@/stores/user'
 import { useCacheStore } from '@/stores/cache'
 
@@ -185,7 +137,6 @@ export default {
     return {
       selection: [],
       consumers: [],
-      table: null,
       items: [
         {
           title: this.$t('navigation.databases'),
@@ -228,7 +179,7 @@ export default {
     database () {
       return this.cacheStore.getDatabase
     },
-    cachedTable () {
+    table () {
       return this.cacheStore.getTable
     },
     roles () {
@@ -243,17 +194,32 @@ export default {
       }
       return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all'
     },
+    canViewSchema () {
+      if (this.error) {
+        return false
+      }
+      if (!this.table) {
+        return false
+      }
+      if (this.table.is_schema_public || this.table.is_public) {
+        return true
+      }
+      if (!this.user) {
+        return false
+      }
+      return this.hasReadAccess || this.table.owner.id === this.user.id || this.database.owner.id === this.user.id
+    },
     canWrite () {
       if (!this.table || !this.user || !this.access) {
         return false
       }
-      return (this.access.type === 'write_own' && this.cachedTable.owned_by === this.user.id) || this.access.type === 'write_all'
+      return (this.access.type === 'write_own' && this.table.owned_by === this.user.id) || this.access.type === 'write_all'
     },
     access () {
       return this.userStore.getAccess
     },
     hasDescription () {
-      return this.table && this.cachedTable.description
+      return this.table && this.table.description
     },
     canWriteQueues () {
       if (!this.roles) {
@@ -317,40 +283,6 @@ export default {
       } else if (this.canRead) {
         return this.$t('pages.table.connection.permissions.read')
       }
-    },
-    databaseVisibility () {
-      if (!this.database) {
-        return null
-      }
-      if (this.database.is_public && this.cachedTable.is_schema_public) {
-        return this.$t('pages.table.visibility.open')
-      }
-      if (!this.database.is_public && !this.cachedTable.is_schema_public) {
-        return this.$t('pages.table.visibility.closed')
-      }
-      return this.database.is_public ? this.$t('pages.database.visibility.data') : this.$t('pages.database.visibility.schema')
-    }
-  },
-  mounted () {
-    this.fetchTable()
-  },
-  methods: {
-    fetchTable () {
-      this.loading = true
-      const tableService = useTableService()
-      tableService.findOne(this.$route.params.database_id, this.$route.params.table_id)
-        .then((table) => {
-          this.loading = false
-          this.table = table
-        })
-        .catch(({code}) => {
-          this.loading = false
-          const toast = useToastInstance()
-          toast.error(this.$t(code))
-        })
-        .finally(() => {
-          this.loading = false
-        })
     }
   }
 }
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue
index 076b46217d4f280e5ded0e1cb94ca8c6c4110881..d444ca4e6229886522e39c0ad8a35b3988fb022e 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/[identifier_id]/index.vue
@@ -1,14 +1,15 @@
 <template>
-  <div v-if="canCreateIdentifier || canUpdateIdentifier">
+  <div
+    v-if="canCreateIdentifier || canUpdateIdentifier">
     <Persist type="table" :database="database" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
 
 <script>
-import Persist from '~/components/identifier/Persist.vue'
-import { useUserStore } from '~/stores/user.js'
-import { useCacheStore } from '~/stores/cache.js'
+import Persist from '@/components/identifier/Persist.vue'
+import { useUserStore } from '@/stores/user'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   components: {
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue
index 8ab4f83a25293ee0d9338f87c0c35b378a123a6f..af5b613df9673387bc4ab660487de4717524ab02 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/persist/index.vue
@@ -12,9 +12,9 @@
 </template>
 
 <script>
-import Persist from '~/components/identifier/Persist.vue'
-import { useUserStore } from '~/stores/user.js'
-import { useCacheStore } from '~/stores/cache.js'
+import Persist from '@/components/identifier/Persist.vue'
+import { useUserStore } from '@/stores/user'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   components: {
@@ -75,5 +75,3 @@ export default {
   }
 }
 </script>
-<style>
-</style>
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue
index 5364b6c8997e4e336852f02747f2b7bfc6b08ee7..0c92401672bf743943dc1f35bdef3d42cd0df3d0 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue
@@ -160,7 +160,7 @@ export default {
       ],
       headers: [
         { value: 'internal_name', title: this.$t('pages.table.subpages.schema.internal-name.title') },
-        { value: 'column_type', title: this.$t('pages.table.subpages.schema.column-type.title') },
+        { value: 'type', title: this.$t('pages.table.subpages.schema.column-type.title') },
         { value: 'extra', title: this.$t('pages.table.subpages.schema.extra.title') },
         { value: 'column_concept', title: this.$t('pages.table.subpages.schema.concept.title') },
         { value: 'column_unit', title: this.$t('pages.table.subpages.schema.unit.title') },
@@ -195,6 +195,9 @@ export default {
       return this.userStore.getRoles
     },
     canViewSchema () {
+      if (this.error) {
+        return false
+      }
       if (!this.table) {
         return false
       }
@@ -204,7 +207,7 @@ export default {
       if (!this.user) {
         return false
       }
-      return this.hasReadAccess || this.table.owned_by === this.user.id || this.database.owner.id === this.user.id
+      return this.hasReadAccess || this.table.owner.id === this.user.id || this.database.owner.id === this.user.id
     },
     primaryKeysColumns () {
       return this.table.constraints.primary_key.map(pk => pk.column.internal_name).join(', ')
@@ -236,9 +239,9 @@ export default {
   },
   methods: {
     extra (column) {
-      if (column.column_type === 'float') {
+      if (column.type === 'float') {
         return `precision=${column.size}`
-      } else if (['decimal', 'double'].includes(column.column_type)) {
+      } else if (['decimal', 'double'].includes(column.type)) {
         let extra = ''
         if (column.size !== null) {
           extra += `size=${column.size}`
@@ -250,11 +253,11 @@ export default {
           extra += `d=${column.d}`
         }
         return extra
-      } else if (column.column_type === 'enum') {
+      } else if (column.type === 'enum') {
         return `(${column.enums.join(', ')})`
-      } else if (column.column_type === 'set') {
+      } else if (column.type === 'set') {
         return `(${column.sets.join(', ')})`
-      } else if (['int', 'char', 'varchar', 'binary', 'varbinary', 'tinyint', 'size="small"int', 'mediumint', 'bigint'].includes(column.column_type)) {
+      } else if (['int', 'char', 'varchar', 'binary', 'varbinary', 'tinyint', 'size="small"int', 'mediumint', 'bigint'].includes(column.type)) {
         return column.size !== null ? `size=${column.size}` : ''
       }
       return null
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue
new file mode 100644
index 0000000000000000000000000000000000000000..201917c5c051ffd63028fdb7f6f631e9b0dafce7
--- /dev/null
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue
@@ -0,0 +1,344 @@
+<template>
+  <div
+    v-if="canUpdateTable">
+    <TableToolbar />
+    <v-window
+      v-if="user"
+      v-model="tab">
+      <v-window-item>
+        <v-form
+          ref="form"
+          v-model="valid"
+          autocomplete="off"
+          @submit.prevent="submit">
+          <v-card
+            variant="flat"
+            rounded="0"
+            :title="$t('pages.table.settings.title')"
+            :subtitle="$t('pages.table.settings.subtitle')">
+            <v-card-text>
+              <v-row>
+                <v-col
+                  md="8">
+                  <v-textarea
+                    v-model="modify.description"
+                    rows="2"
+                    :rules="[
+                      v => !max(v, 180) || ($t('validation.max-length') + 180),
+                    ]"
+                    clearable
+                    counter="180"
+                    persistent-counter
+                    persistent-hint
+                    :variant="inputVariant"
+                    :hint="$t('pages.table.subpages.import.description.hint')"
+                    :label="$t('pages.table.subpages.import.description.label')"/>
+                </v-col>
+              </v-row>
+              <v-row
+                dense>
+                <v-col
+                  md="4">
+                  <v-select
+                    v-model="modify.is_public"
+                    :items="dataOptions"
+                    persistent-hint
+                    :variant="inputVariant"
+                    required
+                    :rules="[
+                      v => v !== null || $t('validation.required')
+                    ]"
+                    :label="$t('pages.database.resource.data.label')"
+                    :hint="$t('pages.database.resource.data.hint', { resource: 'table' })" />
+                </v-col>
+                <v-col
+                  md="4">
+                  <v-select
+                    v-model="modify.is_schema_public"
+                    :items="schemaOptions"
+                    persistent-hint
+                    :variant="inputVariant"
+                    required
+                    :rules="[
+                      v => v !== null || $t('validation.required')
+                    ]"
+                    :label="$t('pages.database.resource.schema.label')"
+                    :hint="$t('pages.database.resource.schema.hint', { resource: 'table', schema: 'columns' })" />
+                </v-col>
+              </v-row>
+              <v-row>
+                <v-col>
+                  <v-btn
+                    id="database"
+                    variant="flat"
+                    size="small"
+                    :disabled="!valid || !isChange"
+                    :color="buttonColor"
+                    :loading="loading"
+                    type="submit"
+                    :text="$t('navigation.modify')"
+                    @click="update" />
+                </v-col>
+              </v-row>
+            </v-card-text>
+          </v-card>
+        </v-form>
+        <v-divider
+          v-if="canDropTable" />
+        <v-card
+          v-if="canDropTable"
+          variant="flat"
+          rounded="0"
+          :title="$t('pages.table.delete.title')"
+          :subtitle="$t('pages.table.delete.subtitle', { table: table.internal_name })">
+          <v-card-text>
+            <v-row>
+              <v-col
+                md="8">
+                <v-btn
+                  size="small"
+                  variant="flat"
+                  color="error"
+                  @click="askDelete">
+                  {{ $t('navigation.delete')}}
+                </v-btn>
+              </v-col>
+            </v-row>
+          </v-card-text>
+        </v-card>
+      </v-window-item>
+    </v-window>
+    <v-breadcrumbs
+      :items="items"
+      class="pa-0 mt-2" />
+  </div>
+</template>
+
+<script>
+import TableToolbar from '@/components/table/TableToolbar.vue'
+import { useUserStore } from '@/stores/user'
+import { useCacheStore } from '@/stores/cache'
+import { max } from '@/utils'
+
+export default {
+  components: {
+    TableToolbar
+  },
+  data () {
+    return {
+      tab: 0,
+      valid: false,
+      loading: false,
+      modify: {
+        description: null,
+        is_public: null,
+        is_schema_public: null
+      },
+      dataOptions: [
+        { title: this.$t('pages.database.resource.data.enabled'), value: true },
+        { title: this.$t('pages.database.resource.data.disabled'), value: false },
+      ],
+      schemaOptions: [
+        { title: this.$t('pages.database.resource.schema.enabled'), value: true },
+        { title: this.$t('pages.database.resource.schema.disabled'), value: false },
+      ],
+      items: [
+        {
+          title: this.$t('navigation.databases'),
+          to: '/database'
+        },
+        {
+          title: `${this.$route.params.database_id}`,
+          to: `/database/${this.$route.params.database_id}/info`
+        },
+        {
+          title: this.$t('navigation.tables'),
+          to: `/database/${this.$route.params.database_id}/table`
+        },
+        {
+          title: `${this.$route.params.table_id}`,
+          to: `/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}`
+        },
+        {
+          title: this.$t('navigation.settings'),
+          to: `/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/settings`,
+          disabled: true
+        }
+      ],
+      headers: [
+        { value: 'internal_name', title: this.$t('pages.table.subpages.schema.internal-name.title') },
+        { value: 'type', title: this.$t('pages.table.subpages.schema.column-type.title') },
+        { value: 'extra', title: this.$t('pages.table.subpages.schema.extra.title') },
+        { value: 'column_concept', title: this.$t('pages.table.subpages.schema.concept.title') },
+        { value: 'column_unit', title: this.$t('pages.table.subpages.schema.unit.title') },
+        { value: 'is_null_allowed', title: this.$t('pages.table.subpages.schema.nullable.title') },
+        { value: 'description', title: this.$t('pages.table.subpages.schema.description.title') },
+      ],
+      dateColumns: [],
+      userStore: useUserStore(),
+      cacheStore: useCacheStore()
+    }
+  },
+  computed: {
+    user () {
+      return this.userStore.getUser
+    },
+    database () {
+      return this.cacheStore.getDatabase
+    },
+    table () {
+      return this.cacheStore.getTable
+    },
+    access () {
+      return this.userStore.getAccess
+    },
+    hasReadAccess () {
+      if (!this.access) {
+        return false
+      }
+      return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own'
+    },
+    roles () {
+      return this.userStore.getRoles
+    },
+    isChange () {
+      if (!this.table) {
+        return false
+      }
+      if (this.table.is_public !== this.modify.is_public) {
+        return true
+      }
+      return this.table.is_schema_public !== this.modify.is_schema_public
+    },
+    canUpdateTable () {
+      if (!this.roles || !this.user || !this.table) {
+        return false
+      }
+      return this.roles.includes('update-table') && this.table.owner.id === this.user.id
+    },
+    canModifyVisibility () {
+      if (!this.roles || !this.user || !this.table) {
+        return false
+      }
+      return this.roles.includes('update-table') && this.table.owner.id === this.user.id
+    },
+    canDropTable () {
+      if (!this.roles || !this.table || !this.user) {
+        return false
+      }
+      if (this.roles.includes('delete-foreign-table')) {
+        return true
+      }
+      const tableService = useTableService()
+      return tableService.isOwner(this.table, this.user) && this.roles.includes('delete-table') && this.table.identifiers.length === 0
+    },
+    inputVariant () {
+      const runtimeConfig = useRuntimeConfig()
+      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal
+    },
+    buttonVariant () {
+      const runtimeConfig = useRuntimeConfig()
+      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
+    },
+    buttonColor () {
+      return !this.isChange ? null : 'warning'
+    }
+  },
+  mounted() {
+    if (!this.table) {
+      return
+    }
+    this.modify.is_public = this.table.is_public
+    this.modify.is_schema_public = this.table.is_schema_public
+    this.modify.description = this.table.description
+  },
+  methods: {
+    max,
+    submit () {
+      this.$refs.form.validate()
+    },
+    extra (column) {
+      if (column.type === 'float') {
+        return `precision=${column.size}`
+      } else if (['decimal', 'double'].includes(column.type)) {
+        let extra = ''
+        if (column.size !== null) {
+          extra += `size=${column.size}`
+        }
+        if (column.d !== null) {
+          if (extra.length > 0) {
+            extra += ', '
+          }
+          extra += `d=${column.d}`
+        }
+        return extra
+      } else if (column.type === 'enum') {
+        return `(${column.enums.join(', ')})`
+      } else if (column.type === 'set') {
+        return `(${column.sets.join(', ')})`
+      } else if (['int', 'char', 'varchar', 'binary', 'varbinary', 'tinyint', 'size="small"int', 'mediumint', 'bigint'].includes(column.type)) {
+        return column.size !== null ? `size=${column.size}` : ''
+      }
+      return null
+    },
+    closed (event) {
+      const { success } = event
+      console.debug('closed dialog', event)
+      if (success) {
+        const toast = useToastInstance()
+        toast.success(this.$t('success.table.semantics'))
+        this.cacheStore.reloadTable()
+      }
+      this.dialogSemantic = false
+    },
+    update () {
+      this.loading = true
+      const tableService = useTableService()
+      tableService.update(this.$route.params.database_id, this.$route.params.table_id, this.modify)
+        .then(() => {
+          this.loading = false
+          const toast = useToastInstance()
+          toast.success(this.$t('success.table.updated', { table: this.table.internal_name }))
+          this.$emit('close', { success: true })
+          this.cacheStore.reloadTable()
+        })
+        .catch(({ code }) => {
+          this.loading = false
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(this.$t(code))
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    askDelete () {
+      if (!confirm(this.$t('pages.table.delete.subtitle', { table: this.table.internal_name }))) {
+        return
+      }
+      this.loadingDelete = true
+      const tableService = useTableService()
+      tableService.remove(this.database.id, this.table.id)
+        .then(() => {
+          console.info('Deleted table with id ', this.table.id)
+          this.cacheStore.reloadDatabase()
+          const toast = useToastInstance()
+          toast.success('Successfully deleted table with id ' + this.table.id)
+          this.$router.push(`/database/${this.$route.params.database_id}/table`)
+        })
+        .catch(({code, message}) => {
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(this.$t(code))
+        })
+        .finally(() => {
+          this.loadingDelete = false
+        })
+    }
+  }
+}
+</script>
diff --git a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue
index 29773c24d98801f1c710a87e2cf513a842f562eb..b031c846ea41b1e3932197401aba95afc9acbb9e 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue
@@ -1,5 +1,6 @@
 <template>
-  <div v-if="canInsertTableData">
+  <div
+    v-if="canInsertTableData">
     <v-toolbar flat>
       <v-btn
         class="mr-2"
@@ -10,214 +11,219 @@
       <v-toolbar-title
         :text="$t('pages.table.subpages.import.title')"/>
     </v-toolbar>
-    <v-card
-      variant="flat"
-      rounded="0">
-      <v-card-text>
-        <v-row>
-          <v-col
-            md="8">
-            <v-alert
-              border="start"
-              color="info">
-              {{ $t('pages.table.subpages.import.dataset.text') }}
-              <NuxtLink
-                :href="`/database/${$route.params.database_id}/table/create/schema`">
-                {{ $t('pages.table.subpages.import.schema.text') }}
-              </NuxtLink>
-            </v-alert>
-          </v-col>
-        </v-row>
-      </v-card-text>
-      <v-card-text>
-        <v-stepper
-          vertical
-          variant="flat">
-          <v-stepper-header>
-            <v-stepper-item
-              :title="$t('pages.table.subpages.import.metadata.title')"
-              :complete="validStep1"
-              :value="1"/>
-          </v-stepper-header>
-          <v-stepper-window
-            direction="vertical">
-            <v-form
-              ref="form"
-              v-model="validStep1"
-              :disabled="step > 4"
-              @submit.prevent="submit">
+    <v-form
+      ref="form"
+      v-model="valid"
+      @submit.prevent="submit">
+      <v-card
+        variant="flat"
+        rounded="0">
+        <v-card-text>
+          <v-row>
+            <v-col
+              md="8">
+              <v-alert
+                border="start"
+                color="info">
+                {{ $t('pages.table.subpages.import.dataset.text') }}
+                <NuxtLink
+                  :href="`/database/${$route.params.database_id}/table/create/schema`">
+                  {{ $t('pages.table.subpages.import.schema.text') }}
+                </NuxtLink>
+              </v-alert>
+            </v-col>
+          </v-row>
+        </v-card-text>
+        <v-card-text>
+          <v-stepper
+            vertical
+            variant="flat">
+            <v-stepper-header>
+              <v-stepper-item
+                :title="$t('pages.table.subpages.import.metadata.title')"
+                :complete="validStep1"
+                :value="1"/>
+            </v-stepper-header>
+            <v-stepper-window
+              direction="vertical">
+              <v-form
+                ref="form"
+                v-model="validStep1"
+                :disabled="step > 4"
+                @submit.prevent="submit">
+                <v-container>
+                  <v-row
+                    dense>
+                    <v-col md="4">
+                      <v-text-field
+                        v-model="tableCreate.name"
+                        :rules="[
+                          v => notEmpty(v) || $t('validation.required'),
+                          v => generatedTableName.length <= 64 || ($t('validation.max-length') + 64),
+                        ]"
+                        required
+                        clearable
+                        :error-messages="!validTableName ? [$t('validation.table.exists')] : []"
+                        persistent-hint
+                        :variant="inputVariant"
+                        :hint="$t('pages.table.subpages.import.name.hint')"
+                        :label="$t('pages.table.subpages.import.name.label')"/>
+                    </v-col>
+                    <v-col md="4">
+                      <v-text-field
+                        v-model="generatedTableName"
+                        :rules="[
+                          v => notEmpty(v) || $t('validation.required'),
+                          v => generatedTableName.length <= 64 || ($t('validation.max-length') + 64),
+                        ]"
+                        disabled
+                        clearable
+                        counter="64"
+                        persistent-counter
+                        persistent-hint
+                        :variant="inputVariant"
+                        :hint="$t('pages.table.subpages.import.generated.hint')"
+                        :label="$t('pages.table.subpages.import.generated.label')"/>
+                    </v-col>
+                  </v-row>
+                  <v-row
+                    dense>
+                    <v-col md="8">
+                      <v-textarea
+                        v-model="tableCreate.description"
+                        rows="2"
+                        :rules="[
+                          v => (!!v || v.length <= 180) || ($t('validation.max-length') + 180),
+                        ]"
+                        clearable
+                        counter="180"
+                        persistent-counter
+                        persistent-hint
+                        :variant="inputVariant"
+                        :hint="$t('pages.table.subpages.import.description.hint')"
+                        :label="$t('pages.table.subpages.import.description.label')"/>
+                    </v-col>
+                  </v-row>
+                  <v-row
+                    dense>
+                    <v-col
+                      md="4">
+                      <v-select
+                        v-model="tableCreate.is_public"
+                        name="public"
+                        :label="$t('pages.database.resource.data.label')"
+                        :hint="$t('pages.database.resource.data.hint', { resource: 'table' })"
+                        persistent-hint
+                        :variant="inputVariant"
+                        :items="dataOptions"
+                        item-title="title"
+                        item-value="value"
+                        :rules="[v => v !== null || $t('validation.required')]"
+                        required>
+                      </v-select>
+                    </v-col>
+                    <v-col
+                      md="4">
+                      <v-select
+                        v-model="tableCreate.is_schema_public"
+                        name="schema-public"
+                        :label="$t('pages.database.resource.schema.label')"
+                        :hint="$t('pages.database.resource.schema.hint', { resource: 'table', schema: 'columns' })"
+                        persistent-hint
+                        :variant="inputVariant"
+                        :items="schemaOptions"
+                        item-title="title"
+                        item-value="value"
+                        :rules="[v => v !== null || $t('validation.required')]"
+                        required>
+                      </v-select>
+                    </v-col>
+                  </v-row>
+                </v-container>
+              </v-form>
+            </v-stepper-window>
+            <TableImport
+              :create="true"
+              :disabled="!validStep1 || step > 4"
+              :table="table"
+              @analyse="onAnalyse"/>
+            <v-stepper-header>
+              <v-stepper-item
+                :title="$t('pages.table.subpages.import.preview.title')"
+                :complete="validStep4"
+                :value="4"/>
+            </v-stepper-header>
+            <v-stepper-window
+              direction="vertical">
+              <v-container
+                v-if="step >= 4">
+                <TableSchema
+                  ref="schema"
+                  :back="false"
+                  :disabled="step > 4"
+                  :loading="loading"
+                  :submit-text="$t('navigation.continue')"
+                  :columns="tableCreate.columns"
+                  @close="createEmptyTableAndImport"/>
+              </v-container>
+            </v-stepper-window>
+            <v-stepper-header>
+              <v-stepper-item
+                :title="$t('pages.table.subpages.import.summary.title')"
+                :value="5"/>
+            </v-stepper-header>
+            <v-stepper-window
+              v-if="step >= 5"
+              direction="vertical">
               <v-container>
-                <v-row
-                  dense>
-                  <v-col md="4">
-                    <v-text-field
-                      v-model="tableCreate.name"
-                      :rules="[
-                        v => notEmpty(v) || $t('validation.required'),
-                        v => generatedTableName.length <= 64 || ($t('validation.max-length') + 64),
-                      ]"
-                      required
-                      clearable
-                      :error-messages="!validTableName ? [$t('validation.table.exists')] : []"
-                      persistent-hint
-                      :variant="inputVariant"
-                      :hint="$t('pages.table.subpages.import.name.hint')"
-                      :label="$t('pages.table.subpages.import.name.label')"/>
-                  </v-col>
-                  <v-col md="4">
-                    <v-text-field
-                      v-model="generatedTableName"
-                      :rules="[
-                        v => notEmpty(v) || $t('validation.required'),
-                        v => generatedTableName.length <= 64 || ($t('validation.max-length') + 64),
-                      ]"
-                      disabled
-                      clearable
-                      counter="64"
-                      persistent-counter
-                      persistent-hint
-                      :variant="inputVariant"
-                      :hint="$t('pages.table.subpages.import.generated.hint')"
-                      :label="$t('pages.table.subpages.import.generated.label')"/>
-                  </v-col>
-                </v-row>
-                <v-row
-                  dense>
-                  <v-col md="8">
-                    <v-textarea
-                      v-model="tableCreate.description"
-                      rows="2"
-                      :rules="[
-                        v => (!!v || v.length <= 180) || ($t('validation.max-length') + 180),
-                      ]"
-                      clearable
-                      counter="180"
-                      persistent-counter
-                      persistent-hint
-                      :variant="inputVariant"
-                      :hint="$t('pages.table.subpages.import.description.hint')"
-                      :label="$t('pages.table.subpages.import.description.label')"/>
-                  </v-col>
-                </v-row>
-                <v-row
-                  dense>
+                <v-row dense>
                   <v-col
-                    md="4">
-                    <v-select
-                      v-model="tableCreate.is_public"
-                      name="public"
-                      :label="$t('pages.database.subpages.create.data.label')"
-                      :hint="$t('pages.database.subpages.create.data.hint')"
-                      persistent-hint
-                      :variant="inputVariant"
-                      :items="visibilityOptions"
-                      item-title="name"
-                      item-value="value"
-                      :rules="[v => v !== null || $t('validation.required')]"
-                      required>
-                    </v-select>
+                    md="8">
+                    <v-alert
+                      border="start"
+                      color="success">
+                      {{ $t('pages.table.subpages.create.summary.text') }}
+                      <strong>
+                        {{ table.internal_name }}
+                      </strong>
+                    </v-alert>
                   </v-col>
-                  <v-col
-                    md="4">
-                    <v-select
-                      v-model="tableCreate.is_schema_public"
-                      name="schema-public"
-                      :label="$t('pages.database.subpages.create.schema.label')"
-                      :hint="$t('pages.database.subpages.create.schema.hint')"
-                      persistent-hint
-                      :variant="inputVariant"
-                      :items="visibilityOptions"
-                      item-title="name"
-                      item-value="value"
-                      :rules="[v => v !== null || $t('validation.required')]"
-                      required>
-                    </v-select>
+                </v-row>
+                <v-row>
+                  <v-col>
+                    <v-btn
+                      class="mb-1 mr-2"
+                      color="tertiary"
+                      size="small"
+                      variant="flat"
+                      :loading="loadingImport"
+                      :text="$t('navigation.import')"
+                      @click="onImport"/>
+                    <v-btn
+                      class="mb-1"
+                      color="secondary"
+                      size="small"
+                      variant="flat"
+                      :loading="loadingContinue"
+                      :text="$t('navigation.view')"
+                      @click="onContinue"/>
                   </v-col>
                 </v-row>
               </v-container>
-            </v-form>
-          </v-stepper-window>
-          <TableImport
-            :create="true"
-            :disabled="!validStep1 || step > 4"
-            :table="table"
-            @analyse="onAnalyse"/>
-          <v-stepper-header>
-            <v-stepper-item
-              :title="$t('pages.table.subpages.import.preview.title')"
-              :complete="validStep4"
-              :value="4"/>
-          </v-stepper-header>
-          <v-stepper-window
-            direction="vertical">
-            <v-container
-              v-if="step >= 4">
-              <TableSchema
-                ref="schema"
-                :back="false"
-                :disabled="step > 4"
-                :loading="loading"
-                :submit-text="$t('navigation.continue')"
-                :columns="tableCreate.columns"
-                @close="createEmptyTableAndImport"/>
-            </v-container>
-          </v-stepper-window>
-          <v-stepper-header>
-            <v-stepper-item
-              :title="$t('pages.table.subpages.import.summary.title')"
-              :value="5"/>
-          </v-stepper-header>
-          <v-stepper-window
-            v-if="step >= 5"
-            direction="vertical">
-            <v-container>
-              <v-row dense>
-                <v-col
-                  md="8">
-                  <v-alert
-                    border="start"
-                    color="success">
-                    {{ $t('pages.table.subpages.create.summary.text') }}
-                    <strong>
-                      {{ table.internal_name }}
-                    </strong>
-                  </v-alert>
-                </v-col>
-              </v-row>
-              <v-row>
-                <v-col>
-                  <v-btn
-                    class="mb-1 mr-2"
-                    color="tertiary"
-                    size="small"
-                    variant="flat"
-                    :loading="loadingImport"
-                    :text="$t('navigation.import')"
-                    @click="onImport"/>
-                  <v-btn
-                    class="mb-1"
-                    color="secondary"
-                    size="small"
-                    variant="flat"
-                    :loading="loadingContinue"
-                    :text="$t('navigation.view')"
-                    @click="onContinue"/>
-                </v-col>
-              </v-row>
-            </v-container>
-          </v-stepper-window>
-        </v-stepper>
-      </v-card-text>
-    </v-card>
+            </v-stepper-window>
+          </v-stepper>
+        </v-card-text>
+      </v-card>
+    </v-form>
     <v-breadcrumbs :items="items" class="pa-0 mt-2"/>
   </div>
 </template>
 
 <script>
 import TableSchema from '@/components/table/TableSchema.vue'
-import {notEmpty} from '@/utils'
-import {useUserStore} from '@/stores/user'
-import {useCacheStore} from '@/stores/cache'
+import { notEmpty } from '@/utils'
+import { useUserStore } from '@/stores/user'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   components: {
@@ -226,6 +232,7 @@ export default {
   data() {
     return {
       step: 1,
+      valid: false,
       validStep1: false,
       validStep2: false,
       validStep3: false,
@@ -235,15 +242,13 @@ export default {
       loadingImport: false,
       fileModel: null,
       rowCount: null,
-      visibilityOptions: [
-        {
-          name: this.$t('toolbars.database.public'),
-          value: true
-        },
-        {
-          name: this.$t('toolbars.database.private'),
-          value: false
-        }
+      dataOptions: [
+        { title: this.$t('pages.database.resource.data.enabled'), value: true },
+        { title: this.$t('pages.database.resource.data.disabled'), value: false },
+      ],
+      schemaOptions: [
+        { title: this.$t('pages.database.resource.schema.enabled'), value: true },
+        { title: this.$t('pages.database.resource.schema.disabled'), value: false },
       ],
       file: {
         filename: null,
@@ -376,7 +381,7 @@ export default {
         .then((table) => {
           this.table = table
           const toast = useToastInstance()
-          toast.success(this.$t('success.table.created'))
+          toast.success(this.$t('success.table.created', { table: table.internal_name }))
           this.step = 5
         })
         .catch(({code, message}) => {
diff --git a/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue
index 458294d1c710c35b6ed2e6e8638ce8710996d890..5f433013bf30d4a450efba6228eb9f693673514f 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue
@@ -1,5 +1,6 @@
 <template>
-  <div v-if="canCreateTable">
+  <div
+    v-if="canCreateTable">
     <v-toolbar
       flat>
       <v-btn
@@ -81,6 +82,41 @@
                       :label="$t('pages.table.subpages.import.description.label')" />
                   </v-col>
                 </v-row>
+                <v-row
+                  dense>
+                  <v-col
+                    md="4">
+                    <v-select
+                      v-model="tableCreate.is_public"
+                      name="public"
+                      :label="$t('pages.database.resource.data.label')"
+                      :hint="$t('pages.database.resource.data.hint', { resource: 'table' })"
+                      persistent-hint
+                      :variant="inputVariant"
+                      :items="dataOptions"
+                      item-title="title"
+                      item-value="value"
+                      :rules="[v => v !== null || $t('validation.required')]"
+                      required>
+                    </v-select>
+                  </v-col>
+                  <v-col
+                    md="4">
+                    <v-select
+                      v-model="tableCreate.is_schema_public"
+                      name="schema-public"
+                      :label="$t('pages.database.resource.schema.label')"
+                      :hint="$t('pages.database.resource.schema.hint', { resource: 'table', schema: 'columns' })"
+                      persistent-hint
+                      :variant="inputVariant"
+                      :items="schemaOptions"
+                      item-title="title"
+                      item-value="value"
+                      :rules="[v => v !== null || $t('validation.required')]"
+                      required>
+                    </v-select>
+                  </v-col>
+                </v-row>
               </v-container>
             </v-form>
           </v-stepper-window>
@@ -171,10 +207,20 @@ export default {
       step: 1,
       table: null,
       error: false,
+      dataOptions: [
+        { title: this.$t('pages.database.resource.data.enabled'), value: true },
+        { title: this.$t('pages.database.resource.data.disabled'), value: false },
+      ],
+      schemaOptions: [
+        { title: this.$t('pages.database.resource.schema.enabled'), value: true },
+        { title: this.$t('pages.database.resource.schema.disabled'), value: false },
+      ],
       tableCreate: {
         name: null,
         description: null,
         columns: [],
+        is_public: true,
+        is_schema_public: true,
         constraints: {
           uniques: [],
           foreign_keys: [],
@@ -253,6 +299,11 @@ export default {
     }
   },
   mounted () {
+    if (!this.database) {
+      return
+    }
+    this.tableCreate.is_public = this.database.is_public
+    this.tableCreate.is_schema_public = this.database.is_schema_public
   },
   methods: {
     notEmpty,
@@ -273,6 +324,9 @@ export default {
         .catch(({code, message}) => {
           this.loading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(`${code}: ${message}`))
         })
         .finally(() => {
diff --git a/dbrepo-ui/pages/database/[database_id]/table/index.vue b/dbrepo-ui/pages/database/[database_id]/table/index.vue
index 7198c88cf31ab8203f711cd8e7982805524ecff9..2f1cf87ec800162f2be19e05916cc99224f26c4a 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/index.vue
@@ -1,15 +1,21 @@
 <template>
-  <div>
+  <div
+    v-if="canViewSchema">
     <DatabaseToolbar />
-    <v-window v-model="tab">
+    <v-window
+      v-model="tab">
       <TableList />
     </v-window>
-    <v-breadcrumbs :items="items" class="pa-0 mt-2" />
+    <v-breadcrumbs
+      :items="items"
+      class="pa-0 mt-2" />
   </div>
 </template>
+
 <script>
 import TableList from '@/components/table/TableList.vue'
 import DatabaseToolbar from '@/components/database/DatabaseToolbar.vue'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   name: 'Tables',
@@ -19,7 +25,7 @@ export default {
   },
   data () {
     return {
-      db: null,
+      tab: 0,
       items: [
         {
           title: this.$t('navigation.databases'),
@@ -34,12 +40,19 @@ export default {
           to: `/database/${this.$route.params.database_id}/table`,
           disabled: true
         }
-      ]
+      ],
+      cacheStore: useCacheStore()
     }
   },
   computed: {
-    tab () {
-      return 1
+    database () {
+      return this.cacheStore.getDatabase
+    },
+    canViewSchema () {
+      if (this.error) {
+        return false
+      }
+      return this.database
     }
   }
 }
diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue
index 9751fce5b7843b27043ea9e103003a01a3dcfbb4..f4c81f43f0bdd08a1984e5cd23c8af8c1bb003e4 100644
--- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue
+++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue
@@ -2,20 +2,20 @@
   <div
     v-if="canReadData">
     <ViewToolbar
-      v-if="cachedView" />
+      v-if="view" />
     <v-toolbar
       color="secondary"
       :title="$t('toolbars.database.current')"
       flat>
       <v-btn
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-download' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-download' : null"
         variant="flat"
         :loading="downloadLoading"
         :text="$t('toolbars.table.data.download')"
         class="mr-2"
         @click.stop="download" />
       <v-btn
-        :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-refresh' : null"
+        :prepend-icon="$vuetify.display.mdAndUp ? 'mdi-refresh' : null"
         variant="flat"
         :text="$t('toolbars.table.data.refresh')"
         class="mr-2"
@@ -37,6 +37,7 @@
 <script>
 import TimeDrift from '@/components/TimeDrift.vue'
 import QueryResults from '@/components/subset/Results.vue'
+import { useUserStore } from '@/stores/user'
 
 export default {
   components: {
@@ -80,11 +81,8 @@ export default {
     database () {
       return this.cacheStore.getDatabase
     },
-    cachedView () {
-      if (!this.database) {
-        return null
-      }
-      return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0]
+    view () {
+      return this.cacheStore.getView
     },
     access () {
       return this.userStore.getAccess
@@ -96,10 +94,10 @@ export default {
       return this.access.type === 'read' ||  this.access.type === 'write_own' ||  this.access.type === 'write_all'
     },
     canReadData () {
-      if (!this.cachedView) {
+      if (!this.view) {
         return false
       }
-      if (this.cachedView.is_public) {
+      if (this.view.is_public) {
         return true
       }
       if (!this.user) {
@@ -135,6 +133,9 @@ export default {
         .catch(({code}) => {
           this.downloadLoading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
         })
         .finally(() => {
diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue
index 364516f45f29209c56748add8bff0da310de7d75..43bfb2b39028857833ba0c15ce09a125141a28f3 100644
--- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue
+++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue
@@ -1,10 +1,11 @@
 <template>
-  <div>
+  <div
+    v-if="canViewView">
     <ViewToolbar />
     <v-window
       v-model="tab">
       <v-window-item
-        v-if="cachedView">
+        v-if="view">
         <v-card variant="flat">
           <Summary
             v-if="hasIdentifier"
@@ -23,15 +24,15 @@
           variant="flat">
           <v-card-text>
             <v-list
-              v-if="cachedView"
+              v-if="view"
               dense>
               <v-list-item
                 :title="$t('pages.view.name.title')">
-                {{ cachedView.internal_name }}
+                {{ view.internal_name }}
               </v-list-item>
               <v-list-item
                 :title="$t('pages.view.query.title')">
-                <pre>{{ cachedView.query }}</pre>
+                <pre>{{ view.query }}</pre>
               </v-list-item>
               <v-list-item
                 :title="$t('pages.view.owner.title')">
@@ -45,34 +46,9 @@
                   width="200" />
               </v-list-item>
               <v-list-item
-                v-if="cachedView.created"
+                v-if="view.created"
                 :title="$t('pages.view.creation.title')">
-                {{ formatUTC(cachedView.created) }}
-              </v-list-item>
-              <v-list-item
-                :title="$t('pages.view.visibility.title')">
-                {{ viewVisibility }}
-              </v-list-item>
-            </v-list>
-          </v-card-text>
-        </v-card>
-        <v-divider />
-        <v-card
-          :title="$t('pages.database.title')"
-          variant="flat">
-          <v-card-text>
-            <v-list dense>
-              <v-list-item
-                :title="$t('pages.database.visibility.title')">
-                {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }}
-              </v-list-item>
-              <v-list-item
-                :title="$t('pages.database.name.title')">
-                <NuxtLink
-                  class="text-primary"
-                  :to="`/database/${database.id}`">
-                  {{ database.internal_name }}
-                </NuxtLink>
+                {{ formatUTC(view.created) }}
               </v-list-item>
             </v-list>
           </v-card-text>
@@ -83,16 +59,6 @@
   </div>
 </template>
 
-<script setup>
-const config = useRuntimeConfig()
-const { database_id, view_id } = useRoute().params
-const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}/view/${view_id}`)
-if (data.value) {
-  const identifierService = useIdentifierService()
-  useServerHead(identifierService.viewToServerHead(data.value))
-  useServerSeoMeta(identifierService.viewToServerSeoMeta(data.value))
-}
-</script>
 <script>
 import ViewToolbar from '@/components/view/ViewToolbar.vue'
 import Summary from '@/components/identifier/Summary.vue'
@@ -113,7 +79,6 @@ export default {
     return {
       tab: 0,
       loadingView: false,
-      view: null,
       items: [
         {
           title: this.$t('navigation.databases'),
@@ -152,15 +117,18 @@ export default {
     database () {
       return this.cacheStore.getDatabase
     },
-    cachedView () {
-      if (!this.database) {
-        return null
-      }
-      return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0]
-    },
     access () {
       return this.userStore.getAccess
     },
+    view () {
+      return this.cacheStore.getView
+    },
+    hasReadAccess () {
+      if (!this.access) {
+        return false
+      }
+      return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own'
+    },
     identifiers () {
       if (!this.view) {
         return []
@@ -174,7 +142,7 @@ export default {
       if (!this.user) {
         return this.identifiers.filter(i => i.status === 'published')
       }
-      return this.identifiers.filter(i => i.status === 'published' || i.creator.id === this.user.id)
+      return this.identifiers.filter(i => i.status === 'published' || i.owner.id === this.user.id)
     },
     identifier () {
       if (this.pid) {
@@ -204,42 +172,22 @@ export default {
       const userService = useUserService()
       return userService.userToFullName(this.view.creator)
     },
-    viewVisibility () {
-      if (!this.cachedView) {
-        return null
+    canViewView () {
+      if (!this.view) {
+        return false
       }
-      if (this.cachedView.is_public && this.cachedView.is_schema_public) {
-        return this.$t('pages.database.visibility.open')
+      if (this.view.is_public) {
+        return true
       }
-      if (!this.cachedView.is_public && !this.cachedView.is_schema_public) {
-        return this.$t('pages.database.visibility.closed')
+      if (!this.user) {
+        return false
       }
-      return this.cachedView.is_public ? this.$t('pages.database.visibility.data') : this.$t('pages.database.visibility.schema')
+      return this.hasReadAccess || this.view.owner.id === this.user.id || this.database.owner.id === this.user.id
     }
   },
-  mounted () {
-    this.fetchView()
-  },
   methods: {
     formatUTC (timestamp) {
       return formatTimestampUTCLabel(timestamp)
-    },
-    fetchView () {
-      this.loadingView = true
-      const viewService = useViewService()
-      viewService.findOne(this.$route.params.database_id, this.$route.params.view_id)
-        .then((view) => {
-          this.view = view
-          this.loadingView = false
-        })
-        .catch(({code}) => {
-          this.loadingView = false
-          const toast = useToastInstance()
-          toast.error(this.$t(code))
-        })
-        .finally(() => {
-          this.loadingView = false
-        })
     }
   }
 }
diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue
index 772910ec0953281912a615bc397790b96fd113e9..86db06f1963fb3e491a939ca389d913db7da473d 100644
--- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/[identifier_id]/index.vue
@@ -1,14 +1,17 @@
 <template>
-  <div v-if="canCreateIdentifier || canUpdateIdentifier">
-    <Persist type="view" :database="database" />
+  <div
+    v-if="canCreateIdentifier || canUpdateIdentifier">
+    <Persist
+      type="view"
+      :database="database" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
 
 <script>
-import Persist from '~/components/identifier/Persist.vue'
-import { useUserStore } from '~/stores/user.js'
-import { useCacheStore } from '~/stores/cache.js'
+import Persist from '@/components/identifier/Persist.vue'
+import { useUserStore } from '@/stores/user'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   components: {
diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue
index a0c91a1a4e04802b699c83c1d6b5de9794997efe..f3a3e18f9df142e3f136caf3d022c218676dfb41 100644
--- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/persist/index.vue
@@ -1,14 +1,18 @@
 <template>
-  <div v-if="canPersistView">
-    <Persist type="view" :database="database" :view="view" />
+  <div
+    v-if="canPersistView">
+    <Persist
+      type="view"
+      :database="database"
+      :view="view" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
 </template>
 
 <script>
-import Persist from '~/components/identifier/Persist.vue'
-import { useUserStore } from '~/stores/user.js'
-import { useCacheStore } from '~/stores/cache.js'
+import Persist from '@/components/identifier/Persist.vue'
+import { useUserStore } from '@/stores/user'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   components: {
@@ -71,5 +75,3 @@ export default {
   }
 }
 </script>
-<style>
-</style>
diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue
index b3352010b26653c7b3cb766734b6eb0a2e64dc44..e126e19d9050c23b4299f9799b2895490813f33f 100644
--- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue
+++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue
@@ -63,7 +63,6 @@ export default {
   data () {
     return {
       loading: false,
-      view: null,
       items: [
         {
           title: this.$t('navigation.databases'),
@@ -89,7 +88,7 @@ export default {
       ],
       headers: [
         { value: 'internal_name', title: this.$t('pages.table.subpages.schema.internal-name.title') },
-        { value: 'column_type', title: this.$t('pages.table.subpages.schema.column-type.title') },
+        { value: 'type', title: this.$t('pages.table.subpages.schema.column-type.title') },
         { value: 'extra', title: this.$t('pages.table.subpages.schema.extra.title') },
         { value: 'column_concept', title: this.$t('pages.table.subpages.schema.concept.title') },
         { value: 'column_unit', title: this.$t('pages.table.subpages.schema.unit.title') },
@@ -100,9 +99,6 @@ export default {
       cacheStore: useCacheStore()
     }
   },
-  mounted () {
-    this.fetchView()
-  },
   computed: {
     user () {
       return this.userStore.getUser
@@ -110,6 +106,9 @@ export default {
     database () {
       return this.cacheStore.getDatabase
     },
+    view () {
+      return this.cacheStore.getView
+    },
     access () {
       return this.userStore.getAccess
     },
@@ -119,23 +118,17 @@ export default {
       }
       return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own'
     },
-    cachedView () {
-      if (!this.database) {
-        return null
-      }
-      return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0]
-    },
     canViewSchema () {
-      if (!this.cachedView) {
+      if (!this.view) {
         return false
       }
-      if (this.cachedView.is_schema_public) {
+      if (this.view.is_schema_public) {
         return true
       }
       if (!this.user) {
         return false
       }
-      return this.hasReadAccess || this.cachedView.owned_by === this.user.id || this.database.owner.id === this.user.id
+      return this.hasReadAccess || this.view.owner.id === this.user.id || this.database.owner.id === this.user.id
     },
     roles () {
       return this.userStore.getRoles
@@ -151,9 +144,9 @@ export default {
   },
   methods: {
     extra (column) {
-      if (column.column_type === 'float') {
+      if (column.type === 'float') {
         return `precision=${column.size}`
-      } else if (['decimal', 'double'].includes(column.column_type)) {
+      } else if (['decimal', 'double'].includes(column.type)) {
         let extra = ''
         if (column.size !== null) {
           extra += `size=${column.size}`
@@ -165,11 +158,11 @@ export default {
           extra += `d=${column.d}`
         }
         return extra
-      } else if (column.column_type === 'enum') {
+      } else if (column.type === 'enum') {
         return `(${column.enums.join(', ')})`
-      } else if (column.column_type === 'set') {
+      } else if (column.type === 'set') {
         return `(${column.sets.join(', ')})`
-      } else if (['int', 'char', 'varchar', 'binary', 'varbinary', 'tinyint', 'size="small"int', 'mediumint', 'bigint'].includes(column.column_type)) {
+      } else if (['int', 'char', 'varchar', 'binary', 'varbinary', 'tinyint', 'size="small"int', 'mediumint', 'bigint'].includes(column.type)) {
         return column.size !== null ? `size=${column.size}` : ''
       }
       return null
@@ -179,23 +172,6 @@ export default {
     },
     hasConcept (item) {
       return item.concept && 'uri' in item.concept
-    },
-    fetchView () {
-      this.loading = true
-      const viewService = useViewService()
-      viewService.findOne(this.$route.params.database_id, this.$route.params.view_id)
-        .then((view) => {
-          this.view = view
-          this.loading = false
-        })
-        .catch(({code}) => {
-          this.loading = false
-          const toast = useToastInstance()
-          toast.error(this.$t(code))
-        })
-        .finally(() => {
-          this.loading = false
-        })
     }
   }
 }
diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/settings.vue
new file mode 100644
index 0000000000000000000000000000000000000000..27444c9e89aec8cb2f9fb915504f3f9a4aaa97f1
--- /dev/null
+++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/settings.vue
@@ -0,0 +1,311 @@
+<template>
+  <div
+    v-if="canViewSettings">
+    <ViewToolbar />
+    <v-form
+      v-if="canUpdateVisibility"
+      ref="form"
+      v-model="valid"
+      autocomplete="off"
+      @submit.prevent="submit">
+      <v-card
+        variant="flat"
+        rounded="0"
+        :title="$t('pages.view.settings.title')"
+        :subtitle="$t('pages.view.settings.subtitle')">
+        <v-card-text>
+          <v-row
+            dense>
+            <v-col
+              md="4">
+              <v-select
+                v-model="modify.is_public"
+                :items="dataOptions"
+                persistent-hint
+                :variant="inputVariant"
+                required
+                :rules="[
+                  v => v !== null || $t('validation.required')
+                ]"
+                :label="$t('pages.database.resource.data.label')"
+                :hint="$t('pages.database.resource.data.hint', { resource: 'view' })" />
+            </v-col>
+            <v-col
+              md="4">
+              <v-select
+                v-model="modify.is_schema_public"
+                :items="schemaOptions"
+                persistent-hint
+                :variant="inputVariant"
+                required
+                :rules="[
+                  v => v !== null || $t('validation.required')
+                ]"
+                :label="$t('pages.database.resource.schema.label')"
+                :hint="$t('pages.database.resource.schema.hint', { resource: 'view', schema: 'query' })" />
+            </v-col>
+          </v-row>
+          <v-row>
+            <v-col>
+              <v-btn
+                variant="flat"
+                size="small"
+                :disabled="!valid || !isChange"
+                :color="buttonColor"
+                :loading="loading"
+                type="submit"
+                :text="$t('navigation.modify')"
+                @click="update" />
+            </v-col>
+          </v-row>
+        </v-card-text>
+      </v-card>
+    </v-form>
+    <v-divider
+      v-if="canDeleteView" />
+    <v-card
+      v-if="canDeleteView"
+      variant="flat"
+      rounded="0"
+      :title="$t('pages.view.delete.title')"
+      :subtitle="$t('pages.view.delete.subtitle', { view: view.internal_name })">
+      <v-card-text>
+        <v-row>
+          <v-col
+            md="8">
+            <v-btn
+              size="small"
+              variant="flat"
+              color="error"
+              @click="askDelete">
+              {{ $t('navigation.delete')}}
+            </v-btn>
+          </v-col>
+        </v-row>
+      </v-card-text>
+    </v-card>
+    <v-breadcrumbs
+      :items="items"
+      class="pa-0 mt-2" />
+  </div>
+</template>
+
+<script>
+import ViewToolbar from '@/components/view/ViewToolbar.vue'
+import { useUserStore } from '@/stores/user'
+import { useCacheStore } from '@/stores/cache'
+
+export default {
+  components: {
+    ViewToolbar
+  },
+  data () {
+    return {
+      valid: false,
+      loading: false,
+      modify: {
+        is_public: null,
+        is_schema_public: null
+      },
+      dataOptions: [
+        { title: this.$t('pages.database.resource.data.enabled'), value: true },
+        { title: this.$t('pages.database.resource.data.disabled'), value: false },
+      ],
+      schemaOptions: [
+        { title: this.$t('pages.database.resource.schema.enabled'), value: true },
+        { title: this.$t('pages.database.resource.schema.disabled'), value: false },
+      ],
+      items: [
+        {
+          title: this.$t('navigation.databases'),
+          to: '/database'
+        },
+        {
+          title: `${this.$route.params.database_id}`,
+          to: `/database/${this.$route.params.database_id}/info`
+        },
+        {
+          title: this.$t('navigation.views'),
+          to: `/database/${this.$route.params.database_id}/view`
+        },
+        {
+          title: `${this.$route.params.view_id}`,
+          to: `/database/${this.$route.params.database_id}/view/${this.$route.params.view_id}`
+        },
+        {
+          title: this.$t('navigation.settings'),
+          to: `/database/${this.$route.params.database_id}/view/${this.$route.params.view_id}/settings`,
+          disabled: true
+        }
+      ],
+      headers: [
+        { value: 'internal_name', title: this.$t('pages.table.subpages.schema.internal-name.title') },
+        { value: 'type', title: this.$t('pages.table.subpages.schema.column-type.title') },
+        { value: 'extra', title: this.$t('pages.table.subpages.schema.extra.title') },
+        { value: 'column_concept', title: this.$t('pages.table.subpages.schema.concept.title') },
+        { value: 'column_unit', title: this.$t('pages.table.subpages.schema.unit.title') },
+        { value: 'is_null_allowed', title: this.$t('pages.table.subpages.schema.nullable.title') },
+        { value: 'description', title: this.$t('pages.table.subpages.schema.description.title') },
+      ],
+      dateColumns: [],
+      userStore: useUserStore(),
+      cacheStore: useCacheStore()
+    }
+  },
+  computed: {
+    user () {
+      return this.userStore.getUser
+    },
+    database () {
+      return this.cacheStore.getDatabase
+    },
+    view () {
+      return this.cacheStore.getView
+    },
+    access () {
+      return this.userStore.getAccess
+    },
+    hasReadAccess () {
+      if (!this.access) {
+        return false
+      }
+      return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own'
+    },
+    roles () {
+      return this.userStore.getRoles
+    },
+    isChange () {
+      if (!this.view) {
+        return false
+      }
+      if (this.view.is_public !== this.modify.is_public) {
+        return true
+      }
+      return this.view.is_schema_public !== this.modify.is_schema_public
+    },
+    canUpdateVisibility () {
+      if (!this.roles || !this.user || !this.view) {
+        return false
+      }
+      return this.roles.includes('modify-view-visibility') && this.view.owner.id === this.user.id
+    },
+    canDeleteView () {
+      if (!this.roles || !this.user || !this.view) {
+        return false
+      }
+      return this.roles.includes('delete-database-view') && this.view.owner.id === this.user.id
+    },
+    canViewSettings () {
+      if (!this.user || !this.view) {
+        return false
+      }
+      return this.view.owner.id === this.user.id
+    },
+    inputVariant () {
+      const runtimeConfig = useRuntimeConfig()
+      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.input.contrast : runtimeConfig.public.variant.input.normal
+    },
+    buttonVariant () {
+      const runtimeConfig = useRuntimeConfig()
+      return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
+    },
+    buttonColor () {
+      return !this.isChange ? null : 'warning'
+    }
+  },
+  mounted() {
+    if (!this.view) {
+      return
+    }
+    this.modify.is_public = this.view.is_public
+    this.modify.is_schema_public = this.view.is_schema_public
+    this.modify.description = this.view.description
+  },
+  methods: {
+    submit () {
+      this.$refs.form.validate()
+    },
+    extra (column) {
+      if (column.type === 'float') {
+        return `precision=${column.size}`
+      } else if (['decimal', 'double'].includes(column.type)) {
+        let extra = ''
+        if (column.size !== null) {
+          extra += `size=${column.size}`
+        }
+        if (column.d !== null) {
+          if (extra.length > 0) {
+            extra += ', '
+          }
+          extra += `d=${column.d}`
+        }
+        return extra
+      } else if (column.type === 'enum') {
+        return `(${column.enums.join(', ')})`
+      } else if (column.type === 'set') {
+        return `(${column.sets.join(', ')})`
+      } else if (['int', 'char', 'varchar', 'binary', 'varbinary', 'tinyint', 'size="small"int', 'mediumint', 'bigint'].includes(column.type)) {
+        return column.size !== null ? `size=${column.size}` : ''
+      }
+      return null
+    },
+    closed (event) {
+      const { success } = event
+      console.debug('closed dialog', event)
+      if (success) {
+        const toast = useToastInstance()
+        toast.success(this.$t('success.table.semantics'))
+        this.cacheStore.reloadTable()
+      }
+      this.dialogSemantic = false
+    },
+    askDelete () {
+      if (!confirm(this.$t('pages.view.delete.subtitle', { view: this.view.internal_name }))) {
+        return
+      }
+      this.loadingDelete = true
+      const viewService = useViewService()
+      viewService.remove(this.database.id, this.view.id)
+        .then(() => {
+          console.info('Deleted view with id ', this.view.id)
+          this.cacheStore.reloadDatabase()
+          const toast = useToastInstance()
+          toast.success('Successfully deleted view with id ' + this.view.id)
+          this.$router.push(`/database/${this.$route.params.database_id}/view`)
+        })
+        .catch(({code, message}) => {
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(this.$t(code))
+        })
+        .finally(() => {
+          this.loadingDelete = false
+        })
+    },
+    update () {
+      this.loading = true
+      const viewService = useViewService()
+      viewService.update(this.$route.params.database_id, this.$route.params.view_id, this.modify)
+        .then(() => {
+          this.loading = false
+          const toast = useToastInstance()
+          toast.success(this.$t('success.view.modified'))
+          this.cacheStore.reloadView()
+        })
+        .catch(({code, message}) => {
+          this.loading = false
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(message)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    }
+  }
+}
+</script>
diff --git a/dbrepo-ui/pages/database/[database_id]/view/create.vue b/dbrepo-ui/pages/database/[database_id]/view/create.vue
index 839b79e243d4ceb7903e8a81ee9da0306d29697a..060d186252e1192b3264af89b92b85293ad14d09 100644
--- a/dbrepo-ui/pages/database/[database_id]/view/create.vue
+++ b/dbrepo-ui/pages/database/[database_id]/view/create.vue
@@ -1,5 +1,6 @@
 <template>
-  <div v-if="canCreateView">
+  <div
+    v-if="canCreateView">
     <Builder mode="view" />
     <v-breadcrumbs :items="items" class="pa-0 mt-2" />
   </div>
@@ -7,6 +8,7 @@
 
 <script>
 import Builder from '@/components/subset/Builder.vue'
+import { useUserStore } from '@/stores/user'
 
 export default {
   components: {
diff --git a/dbrepo-ui/pages/database/[database_id]/view/index.vue b/dbrepo-ui/pages/database/[database_id]/view/index.vue
index dc87510ae887285ac36fc647e8f2a172cb708784..4172797328151393813f683200652c2753e783ed 100644
--- a/dbrepo-ui/pages/database/[database_id]/view/index.vue
+++ b/dbrepo-ui/pages/database/[database_id]/view/index.vue
@@ -1,16 +1,21 @@
 <template>
-  <div>
+  <div
+    v-if="canViewSchema">
     <DatabaseToolbar />
-    <v-window v-model="tab">
+    <v-window
+      v-model="tab">
       <ViewList />
     </v-window>
-    <v-breadcrumbs :items="items" class="pa-0 mt-2" />
+    <v-breadcrumbs
+      :items="items"
+      class="pa-0 mt-2" />
   </div>
 </template>
 
 <script>
 import DatabaseToolbar from '@/components/database/DatabaseToolbar.vue'
 import ViewList from '@/components/view/ViewList.vue'
+import { useCacheStore } from '@/stores/cache'
 
 export default {
   name: 'Views',
@@ -20,7 +25,7 @@ export default {
   },
   data () {
     return {
-      db: null,
+      tab: 0,
       items: [
         {
           title: this.$t('navigation.databases'),
@@ -35,20 +40,20 @@ export default {
           to: `/database/${this.$route.params.database_id}/view`,
           disabled: true
         }
-      ]
+      ],
+      cacheStore: useCacheStore()
     }
   },
   computed: {
-    tab () {
-      return 1
+    database () {
+      return this.cacheStore.getDatabase
+    },
+    canViewSchema () {
+      if (this.error) {
+        return false
+      }
+      return this.database
     }
-  },
-  mounted () {
-  },
-  methods: {
   }
 }
 </script>
-
-<style scoped>
-</style>
diff --git a/dbrepo-ui/pages/index.vue b/dbrepo-ui/pages/index.vue
index 037f5b9410f316cce4e24ec2a9d45c6d6615a741..6e71854c0dc72ce8f68f75772befc72fcbbf3279 100644
--- a/dbrepo-ui/pages/index.vue
+++ b/dbrepo-ui/pages/index.vue
@@ -7,7 +7,7 @@
       <v-spacer />
       <v-btn
         v-if="canCreateDatabase"
-        class="mr-4"
+        class="mr-2"
         prepend-icon="mdi-plus"
         variant="flat"
         :text="$t('toolbars.database.create.text')"
diff --git a/dbrepo-ui/pages/login.vue b/dbrepo-ui/pages/login.vue
index 8a35efe59da3aa75da3ec5f23e4800fe92ea99ef..e1b255ed8bc51e2963bb7fb2065d444ef243b8cf 100644
--- a/dbrepo-ui/pages/login.vue
+++ b/dbrepo-ui/pages/login.vue
@@ -117,7 +117,7 @@ export default {
           userService.findOne(userId)
             .then((user) => {
               const toast = useToastInstance()
-              toast.success(this.$t('success.user.login'))
+              toast.success(this.$t('success.user.login', { username : user.username }))
               switch (user.attributes.theme) {
                 case 'dark':
                   this.$vuetify.theme.global.name = 'tuwThemeDark'
@@ -137,12 +137,18 @@ export default {
             })
             .catch(({code}) => {
               const toast = useToastInstance()
+              if (typeof code !== 'string') {
+                return
+              }
               toast.error(this.$t(code))
             })
         })
         .catch(({code}) => {
           this.loading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
         })
         .finally(() => {
diff --git a/dbrepo-ui/pages/search.vue b/dbrepo-ui/pages/search.vue
index ebe16ecec1c159de639a6c735b8f806dda1da21f..b23c896448e12845d0d4b3f92ce0284b5886bd55 100644
--- a/dbrepo-ui/pages/search.vue
+++ b/dbrepo-ui/pages/search.vue
@@ -8,7 +8,7 @@
       <v-spacer />
       <v-btn
         v-if="canCreateDatabase"
-        class="mr-4"
+        class="mr-2"
         prepend-icon="mdi-plus"
         color="secondary"
         variant="flat"
@@ -27,7 +27,8 @@
       v-if="isDatabaseSearch"
       :loading="loading"
       :databases="results" />
-    <div>
+    <div
+      v-else>
       <v-card
         v-for="(result, idx) in results"
         :key="idx"
@@ -38,10 +39,13 @@
         <v-divider class="mx-4" />
         <v-card-title
           class="text-primary text-decoration-underline">
-          <a v-if="link(result)" :href="link(result)">
+          <a
+            v-if="link(result)"
+            :href="link(result)">
             {{ title(result) }}
           </a>
-          <span v-else>
+          <span
+            v-else>
             {{ title(result) }}
           </span>
         </v-card-title>
@@ -52,6 +56,8 @@
           <div
             v-if="tags(result).length > 0"
             class="mt-2 db-tags">
+            <ResourceStatus
+              :resource="result" />
             <v-chip
               v-for="(tag, i) in tags(result)"
               :key="i"
@@ -64,23 +70,15 @@
         </v-card-text>
       </v-card>
     </div>
-    <v-dialog
-      v-model="createDbDialog"
-      persistent
-      max-width="640">
-      <DatabaseCreate @close="closed" />
-    </v-dialog>
   </div>
 </template>
 
 <script>
-import DatabaseCreate from '@/components/database/DatabaseCreate.vue'
 import AdvancedSearch from '@/components/search/AdvancedSearch.vue'
 import { useUserStore } from '@/stores/user'
 
 export default {
   components: {
-    DatabaseCreate,
     AdvancedSearch
   },
   data () {
@@ -88,7 +86,6 @@ export default {
       results: [],
       type: 'database',
       loading: false,
-      createDbDialog: null,
       userStore: useUserStore()
     }
   },
@@ -134,10 +131,13 @@ export default {
       if (!queryKeys || queryKeys.length !== 1 || !queryKeys.includes('q')) {
         return
       }
+      if (!this.q) {
+        return
+      }
       this.loading = true
       const searchService = useSearchService()
       searchService.fuzzy_search(this.q)
-        .then(({results}) => {
+        .then((results) => {
           this.results = results
           this.loading = false
         })
@@ -196,12 +196,6 @@ export default {
       }
       return this.type === 'identifier'
     },
-    isPublic (item) {
-      if (this.isDatabase(item) || this.isTable(item) || this.isColumn(item) || this.isView(item) || this.isIdentifier(item)) {
-        return item.is_public
-      }
-      return null
-    },
     title (item) {
       if (this.isDatabase(item) || this.isTable(item) || this.isColumn(item) || this.isView(item)) {
         return item.name
@@ -215,7 +209,7 @@ export default {
         }
         return title
       } else if (this.isUser(item)) {
-        return item.creator.qualified_name
+        return item.owner.qualified_name
       }
       return null
     },
@@ -230,7 +224,7 @@ export default {
         }
         return description
       } else if (this.isColumn(item)) {
-        let text = item.column_type
+        let text = item.type
         if (item.size) {
           text += `(${item.size}${item.d ? ',' + item.d : ''})`
         }
@@ -260,10 +254,6 @@ export default {
     },
     tags (item) {
       const tags = []
-      if (this.isPublic(item) === true || this.isPublic(item) === false) {
-        const text = this.isPublic(item) ? this.$t('toolbars.database.public') : this.$t('toolbars.database.private')
-        tags.push({ color: this.isPublic(item) ? 'success' : null, text })
-      }
       if (this.isDatabase(item)) {
       } else if (this.isTable(item)) {
       } else if (this.isColumn(item)) {
@@ -296,25 +286,14 @@ export default {
       } else if (this.isUnit(item)) {
       } else if (this.isConcept(item)) {
       } else if (this.isUser(item)) {
-        if (item.creator.attributes.orcid) {
+        if (item.owner.attributes.orcid) {
           tags.push({ text: 'ORCID', color: 'success' })
         }
       }
       return tags
     },
-    closed (event) {
-      this.dialog = false
-      if (event.success) {
-        this.$router.push(`/database/${event.database_id}/info`)
-      }
-    },
-    onSearchResult ({results, type}) {
+    onSearchResult (results) {
       this.results = results
-      if (!type) {
-        return
-      }
-      console.debug('search for type', type, ':', results)
-      this.type = type
     },
     capitalizeFirstLetter(string) {
       if (!string) {
diff --git a/dbrepo-ui/pages/signup.vue b/dbrepo-ui/pages/signup.vue
index 2548a6dfa9b749853ca24b6784235fc14fcb1b78..54c00602256c7815d8aff0b255214e66836bc727 100644
--- a/dbrepo-ui/pages/signup.vue
+++ b/dbrepo-ui/pages/signup.vue
@@ -130,6 +130,9 @@ export default {
         .catch(({code}) => {
           this.loading = false
           const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
           toast.error(this.$t(code))
         })
         .finally(() => {
diff --git a/dbrepo-ui/pages/user/info.vue b/dbrepo-ui/pages/user/info.vue
index 3501818ca0408474e81a9038f15a84d61fb662ff..d2d1b2c8fe44aba72c45beabacff9ca5fc140c2e 100644
--- a/dbrepo-ui/pages/user/info.vue
+++ b/dbrepo-ui/pages/user/info.vue
@@ -14,11 +14,9 @@
                 <v-col md="6">
                   <v-text-field
                     v-model="model.id"
-                    readonly
+                    disabled
                     :variant="inputVariant"
-                    :label="$t('pages.user.subpages.info.id.label')"
-                    append-inner-icon="mdi-content-copy"
-                    @click:append-inner="copy" />
+                    :label="$t('pages.user.subpages.info.id.label')" />
                 </v-col>
               </v-row>
               <v-row dense>
@@ -284,11 +282,6 @@ export default {
         .finally(() => {
           this.orcidLoading = false
         })
-    },
-    copy () {
-      navigator.clipboard.writeText(this.model.id)
-      const toast = useToastInstance()
-      toast.success(this.$t('success.clipboard.user'))
     }
   }
 }
diff --git a/dbrepo-ui/stores/cache.js b/dbrepo-ui/stores/cache.js
index 5004b1beb0170862f5062627656fcf6e8844dd7a..3574b24d7c6300b884f7874726254e3c805393b3 100644
--- a/dbrepo-ui/stores/cache.js
+++ b/dbrepo-ui/stores/cache.js
@@ -6,6 +6,8 @@ export const useCacheStore = defineStore('cache', {
     return {
       database: null,
       table: null,
+      view: null,
+      subset: null,
       ontologies: [],
       messages: [],
       uploadProgress: null
@@ -14,24 +16,32 @@ export const useCacheStore = defineStore('cache', {
   getters: {
     getDatabase: (state) => state.database,
     getTable: (state) => state.table,
+    getView: (state) => state.view,
+    getSubset: (state) => state.subset,
     getOntologies: (state) => state.ontologies,
     getMessages: (state) => state.messages,
     getUploadProgress: (state) => state.uploadProgress,
   },
   actions: {
-    setDatabase (database) {
+    setDatabase(database) {
       this.database = database
     },
-    setTable (table) {
+    setTable(table) {
       this.table = table
     },
-    setOntologies (ontologies) {
+    setView(view) {
+      this.view = view
+    },
+    setSubset(subset) {
+      this.subset = subset
+    },
+    setOntologies(ontologies) {
       this.ontologies = ontologies
     },
-    setUploadProgress (uploadProgress) {
+    setUploadProgress(uploadProgress) {
       this.uploadProgress = uploadProgress
     },
-    reloadMessages () {
+    reloadMessages() {
       const messageService = useMessageService()
       messageService.findAll('active')
         .then(messages => this.messages = messages)
@@ -39,7 +49,7 @@ export const useCacheStore = defineStore('cache', {
           console.error('Failed to reload messages', error)
         })
     },
-    reloadOntologies () {
+    reloadOntologies() {
       const ontologyService = useOntologyService()
       ontologyService.findAll()
         .then(ontologies => this.ontologies = ontologies)
@@ -47,7 +57,7 @@ export const useCacheStore = defineStore('cache', {
           console.error('Failed to reload ontologies', error)
         })
     },
-    reloadDatabase () {
+    reloadDatabase() {
       const databaseService = useDatabaseService()
       databaseService.findOne(this.database.id)
         .then(database => this.database = database)
@@ -55,7 +65,7 @@ export const useCacheStore = defineStore('cache', {
           console.error('Failed to reload database', error)
         })
     },
-    reloadTable () {
+    reloadTable() {
       const tableService = useTableService()
       tableService.findOne(this.table.database_id, this.table.id)
         .then(table => this.table = table)
@@ -63,19 +73,33 @@ export const useCacheStore = defineStore('cache', {
           console.error('Failed to reload table', error)
         })
     },
-    setRouteDatabase (databaseId) {
-      if (!databaseId) {
-        this.database = null
-        return
-      }
-      const databaseService = useDatabaseService()
-      databaseService.findOne(databaseId)
-        .then(database => this.database = database)
+    reloadView() {
+      const viewService = useViewService()
+      viewService.findOne(this.table.database_id, this.view.id)
+        .then(view => this.view = view)
         .catch((error) => {
-          console.error('Failed to set route database', error)
+          console.error('Failed to reload view', error)
         })
     },
-    setRouteTable (databaseId, tableId) {
+    setRouteDatabase (databaseId) {
+      return new Promise((resolve, reject) => {
+        if (!databaseId) {
+          this.database = null
+          reject()
+        }
+        const databaseService = useDatabaseService()
+        databaseService.findOne(databaseId, true)
+          .then((database) => {
+            this.database = database
+            resolve(database)
+          })
+          .catch((error) => {
+            this.database = null
+            reject(error)
+          })
+      })
+    },
+    setRouteTable(databaseId, tableId) {
       if (!databaseId || !tableId) {
         this.table = null
         console.error('Cannot set route table: missing database id', databaseId, 'or table id', tableId)
@@ -84,9 +108,26 @@ export const useCacheStore = defineStore('cache', {
       const tableService = useTableService()
       tableService.findOne(databaseId, tableId)
         .then(table => this.table = table)
-        .catch((error) => {
-          console.error('Failed to set route table', error)
-        })
+    },
+    setRouteView(databaseId, viewId) {
+      if (!databaseId || !viewId) {
+        this.view = null
+        console.error('Cannot set route view: database view id', databaseId, 'or view id', viewId)
+        return
+      }
+      const viewService = useViewService()
+      viewService.findOne(databaseId, viewId)
+        .then(view => this.view = view)
+    },
+    setRouteSubset(databaseId, subsetId) {
+      if (!databaseId || !subsetId) {
+        this.subset = null
+        console.error('Cannot set route subset: missing database id', databaseId, 'or subset id', subsetId)
+        return
+      }
+      const subsetService = useQueryService()
+      subsetService.findOne(databaseId, subsetId)
+        .then(subset => this.subset = subset)
     }
   },
 })
diff --git a/dbrepo-ui/utils/index.ts b/dbrepo-ui/utils/index.ts
index 4f30d8953405445c152dee0b6afd4dc6b44cba3a..5876f4ec2d456d5d4d96d52ecb054e8e51d9ed9b 100644
--- a/dbrepo-ui/utils/index.ts
+++ b/dbrepo-ui/utils/index.ts
@@ -1,6 +1,7 @@
-import { format } from 'date-fns'
 import moment from 'moment'
-import type {AxiosError} from 'axios'
+import {AxiosError, type AxiosResponse, type InternalAxiosRequestConfig} from 'axios'
+import { format } from 'date-fns'
+import { getStatusText } from 'http-status-codes'
 
 
 export function notEmpty(str: string) {
@@ -10,6 +11,13 @@ export function notEmpty(str: string) {
   return str.trim().length > 0
 }
 
+export function max(str: string, len: number) {
+  if (str === null) {
+    return false
+  }
+  return str.trim().length <= len
+}
+
 export function notFile(files: [File[]]) {
   if (!files) {
     return false
@@ -17,6 +25,43 @@ export function notFile(files: [File[]]) {
   return files.length === 1
 }
 
+export function makeError(status: number, code: string | null, message: string | null ): AxiosError {
+  const config: InternalAxiosRequestConfig = {}
+  const response: AxiosResponse = {
+    data: {
+      status: getStatusText(status).toUpperCase()
+    },
+    status,
+    statusText: getStatusText(status).toUpperCase(),
+    config,
+    headers: {}
+  }
+  return new AxiosError(message, code, undefined, null, response)
+}
+
+export function errorCodeKey(error: AxiosError): any {
+  switch (error?.response?.status) {
+    case 404:
+      return {
+        title: 'error.missing.title',
+        subtitle: 'ERR_' + error?.response?.data?.status,
+        text: 'error.missing.text'
+      }
+    case 403:
+      return {
+        title: 'error.permission.title',
+        subtitle: 'ERR_' + error?.response?.data?.status,
+        text: 'error.permission.text'
+      }
+    default:
+      return {
+        title: 'error.gone.title',
+        subtitle: 'ERR_' + error?.response?.data?.status,
+        text: 'error.gone.text'
+      }
+  }
+}
+
 export function castNumberOptional(str: string): string | number {
   const num = Number(str)
   const ss = String(num)
@@ -1048,6 +1093,14 @@ export function isActiveMessage(message: any) {
 }
 
 export function axiosErrorToApiError(error: AxiosError): ApiErrorDto {
+  if (!error || !('data' in error.response)) {
+    const errorObj: ApiErrorDto = {
+      status: 'NOT_SET',
+      code: 'error.axios.connection',
+      message: ''
+    }
+    return errorObj
+  }
   if (error.code === 'ECONNABORTED') {
     /* timeout */
     const errorObj: ApiErrorDto = {
@@ -1057,15 +1110,7 @@ export function axiosErrorToApiError(error: AxiosError): ApiErrorDto {
     }
     return errorObj
   }
-  if ('data' in error.response) {
-    const errorObj: ApiErrorDto = (error.response.data as ApiErrorDto)
-    return errorObj
-  }
-  const errorObj: ApiErrorDto = {
-    status: error.code ? error.code : 'NOT_SET',
-    code: 'error.axios.connection',
-    message: error.message
-  }
+  const errorObj: ApiErrorDto = (error.response.data as ApiErrorDto)
   return errorObj
 }
 
diff --git a/dbrepo-upload-service/pom.xml b/dbrepo-upload-service/pom.xml
index db626827a1e4b98662866815b8a6cf4c87e2d5c8..f2bea092361406c7f933a5c26f8eadb0ecbcb131 100644
--- a/dbrepo-upload-service/pom.xml
+++ b/dbrepo-upload-service/pom.xml
@@ -11,9 +11,9 @@
     <groupId>at.tuwien</groupId>
     <artifactId>dbrepo-upload-service</artifactId>
     <name>dbrepo-upload-service</name>
-    <version>1.6.0</version>
+    <version>1.6.1</version>
 
-    <url>https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.5/</url>
+    <url>https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6/</url>
     <developers>
         <developer>
             <name>Martin Weise</name>
diff --git a/docker-compose.yml b/docker-compose.yml
index 7159ff58cbb59eec4d114603c71c5ea4c52d51b1..315f6bf1884c1e49d2ac870ab53f50d7a6b8c449 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -303,12 +303,14 @@ services:
       AUTH_SERVICE_CLIENT_SECRET: ${AUTH_SERVICE_CLIENT_SECRET:-MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG}
       AUTH_SERVICE_ENDPOINT: ${AUTH_SERVICE_ENDPOINT:-http://auth-service:8080}
       COLLECTION: ${COLLECTION:-['database','table','column','identifier','unit','concept','user','view']}
+      LOG_LEVEL: ${LOG_LEVEL:-info}
       METADATA_SERVICE_ENDPOINT: ${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}
       OPENSEARCH_HOST: ${OPENSEARCH_HOST:-search-db}
       OPENSEARCH_PORT: ${OPENSEARCH_PORT:-9200}
       OPENSEARCH_USERNAME: ${SEARCH_DB_USERNAME:-admin}
       OPENSEARCH_PASSWORD: ${SEARCH_DB_PASSWORD:-admin}
-      LOG_LEVEL: ${LOG_LEVEL:-info}
+      SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
+      SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
     healthcheck:
       test: curl -sSL localhost:8080/health | grep 'UP' || exit 1
       interval: 10s
@@ -402,11 +404,14 @@ services:
       context: ./dbrepo-search-service/init
       network: host
     environment:
+      LOG_LEVEL: ${LOG_LEVEL:-info}
       METADATA_SERVICE_ENDPOINT: ${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}
       OPENSEARCH_HOST: ${OPENSEARCH_HOST:-search-db}
       OPENSEARCH_PORT: ${OPENSEARCH_PORT:-9200}
       OPENSEARCH_USERNAME: ${SEARCH_DB_USERNAME:-admin}
       OPENSEARCH_PASSWORD: ${SEARCH_DB_PASSWORD:-admin}
+      SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
+      SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
     depends_on:
       dbrepo-search-db:
         condition: service_healthy
diff --git a/helm/dbrepo/Chart.lock b/helm/dbrepo/Chart.lock
index f452f870386be707d92e9f19498b4c24f004e6c1..0ae700f9d6f0a4674036ae4fb26c4aafc6ee1a5f 100644
--- a/helm/dbrepo/Chart.lock
+++ b/helm/dbrepo/Chart.lock
@@ -6,25 +6,25 @@ dependencies:
   repository: https://charts.bitnami.com/bitnami
   version: 21.6.1
 - name: mariadb-galera
-  repository: https://charts.bitnami.com/bitnami
+  repository: oci://registry-1.docker.io/bitnamicharts
   version: 13.2.7
 - name: mariadb-galera
-  repository: https://charts.bitnami.com/bitnami
+  repository: oci://registry-1.docker.io/bitnamicharts
   version: 13.2.7
 - name: rabbitmq
-  repository: https://charts.bitnami.com/bitnami
+  repository: oci://registry-1.docker.io/bitnamicharts
   version: 14.0.0
 - name: seaweedfs
   repository: file://../seaweedfs
   version: 4.2.1
 - name: grafana
-  repository: https://charts.bitnami.com/bitnami
+  repository: oci://registry-1.docker.io/bitnamicharts
   version: 11.4.2
 - name: prometheus
-  repository: https://charts.bitnami.com/bitnami
+  repository: oci://registry-1.docker.io/bitnamicharts
   version: 1.3.22
 - name: nginx
-  repository: https://charts.bitnami.com/bitnami
+  repository: oci://registry-1.docker.io/bitnamicharts
   version: 18.3.1
-digest: sha256:414c043a3751945d7bd5b02fa00ee0464bee7f08efb469e00a5f059cdbff03b5
-generated: "2025-01-01T15:06:18.720941571+01:00"
+digest: sha256:f244730fab10d52050634ce3286413d378b92e4dc97b0ad2951295c0d2971146
+generated: "2025-01-14T16:48:00.637443736+01:00"
diff --git a/helm/dbrepo/Chart.yaml b/helm/dbrepo/Chart.yaml
index 2145c0461e025784e1664f6b537a15456a37f86d..22d1865df5866213c931df8b2b522af33137c1e6 100644
--- a/helm/dbrepo/Chart.yaml
+++ b/helm/dbrepo/Chart.yaml
@@ -7,8 +7,8 @@ description: Helm Chart for installing DBRepo
 sources:
   - https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services
 type: application
-version: "1.6.0"
-appVersion: "1.6.0"
+version: "1.6.1"
+appVersion: "1.6.1"
 keywords:
   - dbrepo
 maintainers:
diff --git a/helm/dbrepo/README.md b/helm/dbrepo/README.md
index ead25253597ec98f167ed0ebc892f09c692969a4..8c03ed1801a819673a1af37dcf6de45a3265d55d 100644
--- a/helm/dbrepo/README.md
+++ b/helm/dbrepo/README.md
@@ -11,7 +11,7 @@ sample [
 for your deployment and update the variables, especially `hostname`.
 
 ```bash
-helm install my-release "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo" --values ./values.yaml --version "1.6.0"
+helm install my-release "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo" --values ./values.yaml --version "1.6.1"
 ```
 
 ## Prerequisites
@@ -28,7 +28,7 @@ helm install my-release "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo"
 To install the chart with the release name `my-release`:
 
 ```bash
-helm install my-release "oci://oci://registry.datalab.tuwien.ac.at/dbrepo/helm" --values ./values.yaml --version "1.6.0"
+helm install my-release "oci://oci://registry.datalab.tuwien.ac.at/dbrepo/helm" --values ./values.yaml --version "1.6.1"
 ```
 
 The command deploys DBRepo on the Kubernetes cluster in the default configuration. The Parameters section lists the
diff --git a/helm/dbrepo/charts/mariadb-galera-13.2.7.tgz b/helm/dbrepo/charts/mariadb-galera-13.2.7.tgz
index 377afe25d2435c5788c26592b39797aa1378e966..92bfce78d9c52d62ca9d7e4816c51dccece8af2b 100644
Binary files a/helm/dbrepo/charts/mariadb-galera-13.2.7.tgz and b/helm/dbrepo/charts/mariadb-galera-13.2.7.tgz differ
diff --git a/helm/dbrepo/charts/prometheus-1.3.22.tgz b/helm/dbrepo/charts/prometheus-1.3.22.tgz
index 3d81a5e625af76257c1a7bc032e889005bc66607..5c26eda5dfb6ec24a8e3cf1b22e1b3aee10d838e 100644
Binary files a/helm/dbrepo/charts/prometheus-1.3.22.tgz and b/helm/dbrepo/charts/prometheus-1.3.22.tgz differ
diff --git a/helm/dbrepo/charts/rabbitmq-14.0.0.tgz b/helm/dbrepo/charts/rabbitmq-14.0.0.tgz
index 39ea3aaef2a94fe507a08242bbfe37209eb9fa53..e858784faeff04c04a34ebad05885197f565c1e0 100644
Binary files a/helm/dbrepo/charts/rabbitmq-14.0.0.tgz and b/helm/dbrepo/charts/rabbitmq-14.0.0.tgz differ
diff --git a/helm/dbrepo/charts/seaweedfs-4.2.1.tgz b/helm/dbrepo/charts/seaweedfs-4.2.1.tgz
index 480c5468d22ef48a84068eb63029f0b45a4d8873..fece394e7c53ae6f207c913485155b278314ad1c 100644
Binary files a/helm/dbrepo/charts/seaweedfs-4.2.1.tgz and b/helm/dbrepo/charts/seaweedfs-4.2.1.tgz differ
diff --git a/helm/dbrepo/files/01-setup-schema.sql b/helm/dbrepo/files/01-setup-schema.sql
index 0a43ef604460e8deb4e4fee4f457c0ca833fc6a2..c9ce89d1be71f4791c5e55dbb7c24f46e979355a 100644
--- a/helm/dbrepo/files/01-setup-schema.sql
+++ b/helm/dbrepo/files/01-setup-schema.sql
@@ -9,6 +9,7 @@ CREATE TABLE IF NOT EXISTS `mdb_users`
     email            character varying(255) NOT NULL,
     orcid            character varying(255),
     affiliation      character varying(255),
+    is_internal      BOOLEAN                NOT NULL DEFAULT FALSE,
     mariadb_password character varying(255) NOT NULL,
     theme            character varying(255) NOT NULL default ('light'),
     language         character varying(3)   NOT NULL default ('en'),
@@ -95,7 +96,6 @@ CREATE TABLE IF NOT EXISTS `mdb_databases`
     PRIMARY KEY (id),
     FOREIGN KEY (cid) REFERENCES mdb_containers (id),
     FOREIGN KEY (owned_by) REFERENCES mdb_users (id),
-    FOREIGN KEY (owned_by) REFERENCES mdb_users (id),
     FOREIGN KEY (contact_person) REFERENCES mdb_users (id)
 ) WITH SYSTEM VERSIONING;
 
@@ -135,7 +135,6 @@ CREATE TABLE IF NOT EXISTS `mdb_tables`
     PRIMARY KEY (ID),
     UNIQUE (tDBID, internal_name),
     FOREIGN KEY (tDBID) REFERENCES mdb_databases (id),
-    FOREIGN KEY (owned_by) REFERENCES mdb_users (id),
     FOREIGN KEY (owned_by) REFERENCES mdb_users (id)
 ) WITH SYSTEM VERSIONING;
 
diff --git a/helm/dbrepo/templates/search-secret.yaml b/helm/dbrepo/templates/search-secret.yaml
index 6bbc747123f6f5aa8d0e669dd991b43baa135a67..08ed410c06442ae4fce27295d129b578f772795a 100644
--- a/helm/dbrepo/templates/search-secret.yaml
+++ b/helm/dbrepo/templates/search-secret.yaml
@@ -24,4 +24,6 @@ stringData:
   OPENSEARCH_PORT: "{{ .Values.searchdb.port }}"
   OPENSEARCH_USERNAME: "{{ .Values.searchdb.security.adminUsername }}"
   OPENSEARCH_PASSWORD: "{{ .Values.searchdb.security.adminPassword }}"
+  SYSTEM_USERNAME: "{{ .Values.identityservice.users }}"
+  SYSTEM_PASSWORD: "{{ .Values.identityservice.userPasswords }}"
 {{- end }}
diff --git a/helm/dbrepo/values.yaml b/helm/dbrepo/values.yaml
index e1a6e87cdc12adc73b29be493f7c065b53555d1b..9afc47022d690ba679276c2cd5b4c76c4e7a34be 100644
--- a/helm/dbrepo/values.yaml
+++ b/helm/dbrepo/values.yaml
@@ -112,7 +112,7 @@ authservice:
   init:
     image:
       ## @skip authservice.init.image.name
-      name: registry.datalab.tuwien.ac.at/dbrepo/auth-service-init:1.6.0
+      name: registry.datalab.tuwien.ac.at/dbrepo/auth-service-init:1.6.1
     ## @param authservice.init.resourcesPreset The container resource preset
     resourcesPreset: "nano"
     ## @param authservice.init.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads)
@@ -383,7 +383,7 @@ analyseservice:
   enabled: true
   image:
     ## @skip analyseservice.image.name
-    name: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.0
+    name: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.1
   ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod
   podSecurityContext:
     ## @param analyseservice.podSecurityContext.enabled Enable pods' Security Context
@@ -444,7 +444,7 @@ metadataservice:
   enabled: true
   image:
     ## @skip metadataservice.image.name
-    name: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.0
+    name: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.1
   ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod
   podSecurityContext:
     ## @param metadataservice.podSecurityContext.enabled Enable pods' Security Context
@@ -541,7 +541,7 @@ dataservice:
   endpoint: http://data-service
   image:
     ## @skip dataservice.image.name
-    name: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.0
+    name: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.1
   ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod
   podSecurityContext:
     ## @param dataservice.podSecurityContext.enabled Enable pods' Security Context
@@ -627,7 +627,7 @@ searchservice:
   endpoint: http://search-service
   image:
     ## @skip searchservice.image.name
-    name: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.0
+    name: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.1
   ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod
   podSecurityContext:
     ## @param searchservice.podSecurityContext.enabled Enable pods' Security Context
@@ -674,7 +674,7 @@ searchservice:
   init:
     image:
       ## @skip searchservice.init.image.name
-      name: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.0
+      name: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.1
     ## @param searchservice.init.resourcesPreset The container resource preset
     resourcesPreset: "nano"
     ## @param searchservice.init.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads)
@@ -735,7 +735,7 @@ storageservice:
   init:
     image:
       ## @skip storageservice.init.image.name
-      name: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.0
+      name: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.1
     s3:
       ## @param storageservice.init.s3.endpoint The S3-capable endpoint the microservice connects to.
       endpoint: http://storage-service-s3:8333
@@ -844,7 +844,7 @@ ui:
   enabled: true
   image:
     ## @skip ui.image.name
-    name: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.0
+    name: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.1
   ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod
   podSecurityContext:
     ## @param ui.podSecurityContext.enabled Enable pods' Security Context
@@ -962,6 +962,9 @@ dashboardservice:
     ## @skip dashboardservice.ldap.secretName
     secretName: dashboard-service-secret
   grafana:
+    updateStrategy:
+      ## @skip dashboardservice.grafana.updateStrategy.type
+      type: Recreate
     ## @skip dashboardservice.grafana.extraEnvVarsSecret
     extraEnvVarsSecret: dashboard-service-secret
     ## @skip dashboardservice.grafana.extraConfigmaps
diff --git a/helm/seaweedfs/Chart.lock b/helm/seaweedfs/Chart.lock
index c973082620a92115ed6d4b5f96147ed8ab5d7984..edcc38c41f0c6b2b35f8d740566918ddb16f17fc 100644
--- a/helm/seaweedfs/Chart.lock
+++ b/helm/seaweedfs/Chart.lock
@@ -4,9 +4,9 @@ dependencies:
   version: 20.2.1
 - name: postgresql
   repository: oci://registry-1.docker.io/bitnamicharts
-  version: 16.3.4
+  version: 16.4.3
 - name: common
   repository: oci://registry-1.docker.io/bitnamicharts
-  version: 2.28.0
-digest: sha256:98a68ef67facda82298174f5bae4e07abd0998e0440002390583a1be97b17aee
-generated: "2025-01-01T15:05:43.198633687+01:00"
+  version: 2.29.0
+digest: sha256:4c967f771b303ca0db9ba2e355790152448c77a05d3f6c69eda6c234bc3f60c6
+generated: "2025-01-17T15:24:18.141765362+01:00"
diff --git a/helm/seaweedfs/charts/common-2.28.0.tgz b/helm/seaweedfs/charts/common-2.28.0.tgz
deleted file mode 100644
index 21c0e76d36a29f77c0b874d747424dfa653b4971..0000000000000000000000000000000000000000
Binary files a/helm/seaweedfs/charts/common-2.28.0.tgz and /dev/null differ
diff --git a/helm/seaweedfs/charts/common-2.29.0.tgz b/helm/seaweedfs/charts/common-2.29.0.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..f36e9e24ec32ce1237d1ee774541c5586fce222d
Binary files /dev/null and b/helm/seaweedfs/charts/common-2.29.0.tgz differ
diff --git a/helm/seaweedfs/charts/mariadb-20.2.1.tgz b/helm/seaweedfs/charts/mariadb-20.2.1.tgz
index 2f55cc750ba3907eebca3545636e8f9c8afd19d2..9bba8eed49e866977396c0076b1a9c5946ec88b1 100644
Binary files a/helm/seaweedfs/charts/mariadb-20.2.1.tgz and b/helm/seaweedfs/charts/mariadb-20.2.1.tgz differ
diff --git a/helm/seaweedfs/charts/postgresql-16.3.4.tgz b/helm/seaweedfs/charts/postgresql-16.3.4.tgz
deleted file mode 100644
index 66db70566af3bfd481f39e80e83ff61e22b9d493..0000000000000000000000000000000000000000
Binary files a/helm/seaweedfs/charts/postgresql-16.3.4.tgz and /dev/null differ
diff --git a/helm/seaweedfs/charts/postgresql-16.4.3.tgz b/helm/seaweedfs/charts/postgresql-16.4.3.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..429f7ed063f6655796792fbe711b027e147ddda4
Binary files /dev/null and b/helm/seaweedfs/charts/postgresql-16.4.3.tgz differ
diff --git a/install.sh b/install.sh
index 1e7520e29bf7ed3ef1a5310a8c7027a47b1ec249..710e9d55a1d65ba99d66a1540180548614464559 100644
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # preset
-VERSION="1.6.0"
+VERSION="1.6.1"
 MIN_CPU=8
 MIN_RAM=4
 MIN_MAP_COUNT=262144
diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py
index f10cfdc0fb66b1a25fd92f92b2737d4a9dae73a8..bc6940b7d034bcb85fec9f9c31c3b5a8aff0dcc3 100644
--- a/lib/python/dbrepo/RestClient.py
+++ b/lib/python/dbrepo/RestClient.py
@@ -1,18 +1,17 @@
+import logging
 import os
 import sys
-import logging
 import time
 
 import requests
-from pydantic import TypeAdapter
-from tusclient.client import TusClient
 from pandas import DataFrame
+from pydantic import TypeAdapter
 
 from dbrepo.UploadClient import UploadClient
 from dbrepo.api.dto import *
 from dbrepo.api.exceptions import ResponseCodeError, UsernameExistsError, EmailExistsError, NotExistsError, \
     ForbiddenError, MalformedError, NameExistsError, QueryStoreError, ExternalSystemError, \
-    AuthenticationError, UploadError, FormatNotAvailable, RequestError, ServiceError, ServiceConnectionError
+    AuthenticationError, FormatNotAvailable, RequestError, ServiceError, ServiceConnectionError
 
 logging.basicConfig(format='%(asctime)s %(name)-12s %(levelname)-6s %(message)s', level=logging.INFO,
                     stream=sys.stdout)
@@ -172,7 +171,7 @@ class RestClient:
         raise ResponseCodeError(f'Failed to find users: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
-    def get_units(self) -> List[Unit]:
+    def get_units(self) -> List[UnitBrief]:
         """
         Get all units known to the metadata database.
 
@@ -184,7 +183,7 @@ class RestClient:
         response = self._wrapper(method="get", url=url)
         if response.status_code == 200:
             body = response.json()
-            return TypeAdapter(List[Unit]).validate_python(body)
+            return TypeAdapter(List[UnitBrief]).validate_python(body)
         raise ResponseCodeError(f'Failed to find units: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
@@ -1940,7 +1939,7 @@ class RestClient:
         raise ResponseCodeError(f'Failed to get licenses: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
-    def get_concepts(self) -> List[Concept]:
+    def get_concepts(self) -> List[ConceptBrief]:
         """
         Get list of concepts known to the metadata database.
 
@@ -1950,7 +1949,7 @@ class RestClient:
         response = self._wrapper(method="get", url=url)
         if response.status_code == 200:
             body = response.json()
-            return TypeAdapter(List[Concept]).validate_python(body)
+            return TypeAdapter(List[ConceptBrief]).validate_python(body)
         raise ResponseCodeError(f'Failed to get concepts: response code: {response.status_code} is not '
                                 f'200 (OK): {response.text}')
 
diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py
index 5100a7ae4e7c84ba16e51750a88b15ae5406d381..fa7eb063fc453c3dd8fabef04f141d07012c94a3 100644
--- a/lib/python/dbrepo/api/dto.py
+++ b/lib/python/dbrepo/api/dto.py
@@ -5,7 +5,7 @@ from dataclasses import field
 from enum import Enum
 from typing import List, Optional, Annotated
 
-from pydantic import BaseModel, PlainSerializer, Field
+from pydantic import BaseModel, PlainSerializer
 
 Timestamp = Annotated[
     datetime.datetime, PlainSerializer(lambda v: v.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z', return_type=str)
@@ -117,7 +117,7 @@ class ColumnBrief(BaseModel):
     database_id: int
     table_id: int
     internal_name: str
-    column_type: ColumnType
+    type: ColumnType
 
 
 class TableBrief(BaseModel):
@@ -656,7 +656,7 @@ class ViewBrief(BaseModel):
     owned_by: str
 
 
-class Concept(BaseModel):
+class ConceptBrief(BaseModel):
     id: int
     uri: str
     name: Optional[str] = None
@@ -691,7 +691,7 @@ class TableStatistics(BaseModel):
     columns: dict[str, ColumnStatistic]
 
 
-class Unit(BaseModel):
+class UnitBrief(BaseModel):
     id: int
     uri: str
     name: Optional[str] = None
@@ -886,18 +886,18 @@ class Column(BaseModel):
     name: str
     database_id: int
     table_id: int
+    ord: int
     internal_name: str
-    column_type: ColumnType
-    is_public: bool
     is_null_allowed: bool
+    type: ColumnType
     alias: Optional[str] = None
     description: Optional[str] = None
     size: Optional[int] = None
     d: Optional[int] = None
     mean: Optional[float] = None
     median: Optional[float] = None
-    concept: Optional[Concept] = None
-    unit: Optional[Unit] = None
+    concept: Optional[ConceptBrief] = None
+    unit: Optional[UnitBrief] = None
     enums: Optional[List[str]] = field(default_factory=list)
     sets: Optional[List[str]] = field(default_factory=list)
     index_length: Optional[int] = None
@@ -915,16 +915,15 @@ class ViewColumn(BaseModel):
     name: str
     database_id: int
     internal_name: str
-    column_type: ColumnType
-    is_public: bool
+    type: ColumnType
     is_null_allowed: bool
     alias: Optional[str] = None
     size: Optional[int] = None
     d: Optional[int] = None
     mean: Optional[float] = None
     median: Optional[float] = None
-    concept: Optional[Concept] = None
-    unit: Optional[Unit] = None
+    concept: Optional[ConceptBrief] = None
+    unit: Optional[UnitBrief] = None
     index_length: Optional[int] = None
     length: Optional[int] = None
 
@@ -988,9 +987,10 @@ class DatabaseBrief(BaseModel):
     internal_name: str
     description: Optional[str] = None
     is_public: bool
+    is_schema_public: bool
     identifiers: Optional[List[Identifier]] = field(default_factory=list)
     contact: UserBrief
-    owner: UserBrief
+    owner_id: str
 
 
 class Unique(BaseModel):
diff --git a/lib/python/docs/index.rst b/lib/python/docs/index.rst
index 9c2ec5c590f65d75bcc2a2570146b8bb3e68cbcd..0a989f321fa9dbb644fba8c668cff807a1232826 100644
--- a/lib/python/docs/index.rst
+++ b/lib/python/docs/index.rst
@@ -6,7 +6,7 @@ Pandas `DataFrame <https://pandas.pydata.org/docs/reference/api/pandas.DataFrame
 provides an object-oriented API as well as low-level access to DBRepo services.
 
 .. note::
-   The SDK has been implemented and documented for DBRepo version 1.6.0, earlier versions may be supported but are not tested for compatibility.
+   The SDK has been implemented and documented for DBRepo version 1.6.1, earlier versions may be supported but are not tested for compatibility.
 
 Quickstart
 ----------
diff --git a/lib/python/pyproject.toml b/lib/python/pyproject.toml
index 382134d4df17fb712fb1c76b03bd47abf43625a8..33d1e52cc0a967a56e7e0a9ce639e5ceb53b42c6 100644
--- a/lib/python/pyproject.toml
+++ b/lib/python/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "dbrepo"
-version = "1.6.0"
+version = "1.6.1"
 description = "DBRepo Python Library"
 keywords = [
     "DBRepo",
diff --git a/lib/python/setup.py b/lib/python/setup.py
index 705a48f08a84210af724a50c48acf174f041411b..027b8a5bb628da1cd03378d59d44ff8dc507c9a7 100644
--- a/lib/python/setup.py
+++ b/lib/python/setup.py
@@ -2,7 +2,7 @@
 from distutils.core import setup
 
 setup(name="dbrepo",
-      version="1.6.0",
+      version="1.6.1",
       description="A library for communicating with DBRepo",
       url="https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6/",
       author="Martin Weise",
diff --git a/lib/python/tests/test_unit_database.py b/lib/python/tests/test_unit_database.py
index affa94b496b1a8bf104fb12264256ad91e6fc8fb..eeeea68832ac33e2f013a3c2deaa3d4eec33122c 100644
--- a/lib/python/tests/test_unit_database.py
+++ b/lib/python/tests/test_unit_database.py
@@ -24,10 +24,11 @@ class DatabaseUnitTest(unittest.TestCase):
             DatabaseBrief(
                 id=1,
                 name='test',
-                owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
+                owner_id='8638c043-5145-4be8-a3e4-4b79991b0a16',
                 contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
                 internal_name='test_abcd',
-                is_public=True)
+                is_public=True,
+                is_schema_public=True)
         ]
         with requests_mock.Mocker() as mock:
             # mock
diff --git a/lib/python/tests/test_unit_table.py b/lib/python/tests/test_unit_table.py
index 5d9e35294812c08adab7931c32337754fa17e3ee..4c4ad2af6aabbe6a36689fe3a44b50b8f6f625dc 100644
--- a/lib/python/tests/test_unit_table.py
+++ b/lib/python/tests/test_unit_table.py
@@ -2,14 +2,12 @@ import json
 import unittest
 
 import requests_mock
-import datetime
-
-from dbrepo.RestClient import RestClient
 from pandas import DataFrame
 
-from dbrepo.api.dto import Table, CreateTableConstraints, UserAttributes, User, Column, Constraints, ColumnType, \
-    Concept, Unit, TableStatistics, ColumnStatistic, PrimaryKey, TableMinimal, ColumnMinimal, TableBrief, UserBrief
-from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, NameExistsError, QueryStoreError, \
+from dbrepo.RestClient import RestClient
+from dbrepo.api.dto import Table, CreateTableConstraints, Column, Constraints, ColumnType, ConceptBrief, UnitBrief, \
+    TableStatistics, ColumnStatistic, PrimaryKey, TableMinimal, ColumnMinimal, TableBrief, UserBrief
+from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, NameExistsError, \
     AuthenticationError, ExternalSystemError
 
 
@@ -35,13 +33,14 @@ class TableUnitTest(unittest.TestCase):
                                                                     column=ColumnMinimal(id=1, table_id=2,
                                                                                          database_id=1))]),
                     columns=[Column(id=1,
+                                    ord=0,
                                     name="ID",
                                     database_id=1,
                                     table_id=2,
                                     internal_name="id",
                                     auto_generated=True,
                                     is_primary_key=True,
-                                    column_type=ColumnType.BIGINT,
+                                    type=ColumnType.BIGINT,
                                     is_public=True,
                                     is_null_allowed=False)])
         with requests_mock.Mocker() as mock:
@@ -165,12 +164,13 @@ class TableUnitTest(unittest.TestCase):
                                                                                              database_id=1))]),
                         columns=[Column(id=1,
                                         name="ID",
+                                        ord=0,
                                         database_id=1,
                                         table_id=2,
                                         internal_name="id",
                                         auto_generated=True,
                                         is_primary_key=True,
-                                        column_type=ColumnType.BIGINT,
+                                        type=ColumnType.BIGINT,
                                         is_public=True,
                                         is_null_allowed=False)])
             # mock
@@ -487,20 +487,21 @@ class TableUnitTest(unittest.TestCase):
     def test_update_table_column_succeeds(self):
         with requests_mock.Mocker() as mock:
             exp = Column(id=1,
+                         ord=0,
                          name="ID",
                          database_id=1,
                          table_id=2,
                          internal_name="id",
                          auto_generated=True,
                          is_primary_key=True,
-                         column_type=ColumnType.BIGINT,
+                         type=ColumnType.BIGINT,
                          is_public=True,
-                         concept=Concept(id=2,
-                                         uri="http://dbpedia.org/page/Category:Precipitation",
-                                         name="Precipitation"),
-                         unit=Unit(id=2,
-                                   uri="http://www.wikidata.org/entity/Q119856947",
-                                   name="liters per square meter"),
+                         concept=ConceptBrief(id=2,
+                                              uri="http://dbpedia.org/page/Category:Precipitation",
+                                              name="Precipitation"),
+                         unit=UnitBrief(id=2,
+                                        uri="http://www.wikidata.org/entity/Q119856947",
+                                        name="liters per square meter"),
                          is_null_allowed=False)
             # mock
             mock.put('/api/database/1/table/2/column/1', json=exp.model_dump(), status_code=202)
diff --git a/lib/python/tests/test_unit_view.py b/lib/python/tests/test_unit_view.py
index 865fc6a7148ec642b6bbca3cea3a23c9fc6b9896..52ea405b28369e20244b9fa111cb745580fb9d30 100644
--- a/lib/python/tests/test_unit_view.py
+++ b/lib/python/tests/test_unit_view.py
@@ -5,7 +5,7 @@ import requests_mock
 from pandas import DataFrame
 
 from dbrepo.RestClient import RestClient
-from dbrepo.api.dto import UserAttributes, User, View, ViewColumn, ColumnType, UserBrief
+from dbrepo.api.dto import View, ViewColumn, ColumnType, UserBrief
 from dbrepo.api.exceptions import ForbiddenError, NotExistsError, MalformedError, AuthenticationError
 
 
@@ -31,8 +31,15 @@ class ViewUnitTest(unittest.TestCase):
                         owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
                         is_public=True,
                         is_schema_public=True,
-                        columns=[ViewColumn(id=1, name="id", internal_name="id", database_id=1, auto_generated=False,
-                                            column_type=ColumnType.BIGINT, is_public=True, is_null_allowed=False)],
+                        columns=[ViewColumn(id=1,
+                                            ord=0,
+                                            name="id",
+                                            internal_name="id",
+                                            database_id=1,
+                                            auto_generated=False,
+                                            type=ColumnType.BIGINT,
+                                            is_public=True,
+                                            is_null_allowed=False)],
                         identifiers=[])]
             # mock
             mock.get('/api/database/1/view', json=[exp[0].model_dump()])
@@ -62,8 +69,15 @@ class ViewUnitTest(unittest.TestCase):
                        owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
                        is_public=True,
                        is_schema_public=True,
-                       columns=[ViewColumn(id=1, name="id", internal_name="id", database_id=1, auto_generated=False,
-                                           column_type=ColumnType.BIGINT, is_public=True, is_null_allowed=False)],
+                       columns=[ViewColumn(id=1,
+                                           ord=0,
+                                           name="id",
+                                           internal_name="id",
+                                           database_id=1,
+                                           auto_generated=False,
+                                           type=ColumnType.BIGINT,
+                                           is_public=True,
+                                           is_null_allowed=False)],
                        identifiers=[])
             # mock
             mock.get('/api/database/1/view/3', json=exp.model_dump())
@@ -103,8 +117,15 @@ class ViewUnitTest(unittest.TestCase):
                        owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'),
                        is_public=True,
                        is_schema_public=True,
-                       columns=[ViewColumn(id=1, name="id", internal_name="id", database_id=1, auto_generated=False,
-                                           column_type=ColumnType.BIGINT, is_public=True, is_null_allowed=False)],
+                       columns=[ViewColumn(id=1,
+                                           ord=0,
+                                           name="id",
+                                           internal_name="id",
+                                           database_id=1,
+                                           auto_generated=False,
+                                           type=ColumnType.BIGINT,
+                                           is_public=True,
+                                           is_null_allowed=False)],
                        identifiers=[])
             # mock
             mock.post('/api/database/1/view', json=exp.model_dump(), status_code=201)
diff --git a/make/rel.mk b/make/rel.mk
index 6a24d75a90a3abe6a887262b285262135c1688ce..0c4b2bb193f3618e3098d13fd283882701e29a20 100644
--- a/make/rel.mk
+++ b/make/rel.mk
@@ -6,6 +6,7 @@ tag-images: build-images ## Tag the docker images.
 	docker tag dbrepo-dashboard-service:latest "${REPOSITORY_URL}/dashboard-service:${APP_VERSION}"
 	docker tag dbrepo-ui:latest "${REPOSITORY_URL}/ui:${APP_VERSION}"
 	docker tag dbrepo-data-service:latest "${REPOSITORY_URL}/data-service:${APP_VERSION}"
+	docker tag dbrepo-auth-service-init:latest "${REPOSITORY_URL}/auth-service-init:${APP_VERSION}"
 	docker tag dbrepo-metadata-service:latest "${REPOSITORY_URL}/metadata-service:${APP_VERSION}"
 	docker tag dbrepo-search-db:latest "${REPOSITORY_URL}/search-db:${APP_VERSION}"
 	docker tag dbrepo-search-service:latest "${REPOSITORY_URL}/search-service:${APP_VERSION}"
@@ -18,6 +19,7 @@ release-images: tag-images ## Release the docker images.
 	docker push "${REPOSITORY_URL}/dashboard-service:${APP_VERSION}"
 	docker push "${REPOSITORY_URL}/ui:${APP_VERSION}"
 	docker push "${REPOSITORY_URL}/data-service:${APP_VERSION}"
+	docker push "${REPOSITORY_URL}/auth-service-init:${APP_VERSION}"
 	docker push "${REPOSITORY_URL}/search-db:${APP_VERSION}"
 	docker push "${REPOSITORY_URL}/metadata-service:${APP_VERSION}"
 	docker push "${REPOSITORY_URL}/search-service:${APP_VERSION}"
diff --git a/sonar-project.properties b/sonar-project.properties
index fdbf030ef0e674288083ee1366822ea99d055e19..d354d54608a75063e0000cd577469dba69ae5eec 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -2,7 +2,7 @@
 sonar.projectKey=fair-data-austria-db-repository_fda-services_a57fa043-ab99-4cdd-a721-162d9a916d77
 sonar.host.url=https://s39.datalab.tuwien.ac.at
 # project
-sonar.projectVersion=1.6.0
+sonar.projectVersion=1.6.1
 # general
 sonar.qualitygate.wait=true
 sonar.projectCreation.mainBranchName=master