diff --git a/.docs/changelog.md b/.docs/changelog.md
index e1452dd12c4d6a6de98438087f61a1853e90779f..3e3d479073ca75622e172a659e038b686ba7b1f1 100644
--- a/.docs/changelog.md
+++ b/.docs/changelog.md
@@ -2,7 +2,7 @@
 author: Martin Weise
 ---
 
-## v1.6.4 (2025-02-13)
+## v1.6.4 (2025-02-14)
 
 [:simple-gitlab: GitLab Release](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/tags/v1.6.4)
 
@@ -10,8 +10,8 @@ author: Martin Weise
 
 #### Fixes
 
-* Fixed a bug where the users were not synced with the Metadata Database and the API Password was not recommended on 
-  first login in [#489](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/489).
+* Fixed a bug where the users were not synced with the Metadata Database
+  in [#489](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/489).
 
 ## v1.6.3 (2025-02-05)
 
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 74cfb6df9a689e5a4a70469369c11a9da34cf8ea..5a47aa19e5568c17770f12645ec0d504c8f885da 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -348,6 +348,10 @@ test-search-service:
   before_script:
     - "cp -r ./dbrepo-search-service/init/clients ./dbrepo-search-service/clients"
     - "cp -r ./dbrepo-search-service/init/omlib ./dbrepo-search-service/omlib"
+    - "cp -r ./dbrepo-search-service/init/test/rsa ./dbrepo-search-service/test/rsa"
+    - "cp ./dbrepo-search-service/init/test/test_keycloak_client.py ./dbrepo-search-service/test"
+    - "cp ./dbrepo-search-service/init/test/test_opensearch_client.py ./dbrepo-search-service/test"
+    - "cp ./dbrepo-search-service/init/friendly_names_overrides.json ./dbrepo-search-service/friendly_names_overrides.json"
   script:
     - "pip install pipenv"
     - "pipenv install gunicorn && pipenv install --dev --system --deploy"
@@ -375,7 +379,7 @@ test-search-service-init:
   script:
     - "pip install pipenv"
     - "pipenv install gunicorn && pipenv install --dev --system --deploy"
-    - cd ./dbrepo-search-service/init/ && coverage run -m pytest test/test_app.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt
+    - cd ./dbrepo-search-service/init/ && coverage run -m pytest test/test_app.py test/test_keycloak_client.py test/test_opensearch_client.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt
     - "cat ./coverage.txt | grep -o 'TOTAL[^%]*%'"
   artifacts:
     when: always
diff --git a/dbrepo-auth-service/init/.coveragerc b/dbrepo-auth-service/init/.coveragerc
new file mode 100644
index 0000000000000000000000000000000000000000..5951051c8d9d6377182ca2b4090cb9008691dfba
--- /dev/null
+++ b/dbrepo-auth-service/init/.coveragerc
@@ -0,0 +1,4 @@
+[report]
+
+exclude_lines =
+    if __name__ == .__main__.:
\ No newline at end of file
diff --git a/dbrepo-auth-service/init/.gitignore b/dbrepo-auth-service/init/.gitignore
index 83faa6688b2694e31b9e7f1add69479402c46f27..fc979e485600981f9d2b470ea02d16ff0cd88695 100644
--- a/dbrepo-auth-service/init/.gitignore
+++ b/dbrepo-auth-service/init/.gitignore
@@ -1,5 +1,7 @@
 # IDE
 .idea/
+
 # artifacts
 .coverage
-report.xml
\ No newline at end of file
+coverage.txt
+report.xml
diff --git a/dbrepo-auth-service/init/Pipfile b/dbrepo-auth-service/init/Pipfile
index f7b4b54ee1508ece7d4af92acee907edf990c4c4..58eea6e6a6ac0f29fc61570046b07a86e70e3364 100644
--- a/dbrepo-auth-service/init/Pipfile
+++ b/dbrepo-auth-service/init/Pipfile
@@ -10,6 +10,7 @@ mariadb = "*"
 [dev-packages]
 coverage = "*"
 pytest = "*"
+testcontainers-mysql = "*"
 requests-mock = "*"
 
 [requires]
diff --git a/dbrepo-auth-service/init/Pipfile.lock b/dbrepo-auth-service/init/Pipfile.lock
index 57631a05559948613a5c9a63b37463c95a48da9a..32613b01176fa433f4723298fe45c598e17e7fa3 100644
--- a/dbrepo-auth-service/init/Pipfile.lock
+++ b/dbrepo-auth-service/init/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "d87ee7c247235a203e58f7d0133891024afc72a1331d3d4b2edb01a401bb0c1a"
+            "sha256": "e3dbdaa3974910c1b12957342fa5b2227b215c4e0a774f836b242bc6925cf90c"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -132,20 +132,20 @@
         },
         "mariadb": {
             "hashes": [
-                "sha256:0f8de8d66ca71bd102f34a970a331b7d75bdf7f8050d80e37cdcc6ff3c85cf7a",
-                "sha256:2e72ea65f1d7d8563ee84e172f2a583193092bdb6ff83c470ca9722873273ecc",
-                "sha256:3f64b520089cb60c4f8302f365ed0ae057c4c859ab70fc8b1c4358192c3c8f27",
-                "sha256:579420293fa790d5ae0a6cb4bdb7e8be8facc2ceefb6123c2b0e8042b3fa725d",
-                "sha256:6f28d8ccc597a3a1368be14078110f743900dbb3b0c7f1cce3072d83bec59c8a",
-                "sha256:c1992ebf9c6f012ac158e33fef9f2c4ba899f721064c4ae3a3489233793296c0",
-                "sha256:cf6647cee081e21d0994b409ba8c8fa2077f3972f1de3627c5502fb31d14f806",
-                "sha256:d7302ccd15f0beee7b286885cbf6ac71ddc240374691d669784d99f89ba34d79",
-                "sha256:dbc4cf0e302ca82d46f9431a0b04f048e9c21ee56d6f3162c29605f84d63b40c",
-                "sha256:e94f1738bec09c97b601ddbb1908eb24524ba4630f507a775d82ffdb6c5794b3",
-                "sha256:f6dfdc954edf02b6519419a054798cda6034dc459d1d482e3329e37aa27d34f0"
+                "sha256:194045d2f59b2c9100dad210dbd8ba3008120f8a434e792dae15b711e5449022",
+                "sha256:50b02ff2c78b1b4f4628a054e3c8c7dd92972137727a5cc309a64c9ed20c878c",
+                "sha256:5ae99af48ba92e3e2edad1dcda293f352c1b65e335007943b9def4cdde622fab",
+                "sha256:69b284ed12e6ef8dda6314cb1ca9d20d53b8a32a07bed2924beb25488e7d9502",
+                "sha256:8b49fc1e8c38bf5a779d46f8cea61c99660adb08d5d15cb7dbc7911a6439ffc2",
+                "sha256:8cc4068bdd14f4907746ec3bb37005682e47f84438cf56bdc6d901c2587f792a",
+                "sha256:992064971e0ff4b4154b80b16589ec86237ce4bd33debe1be5212141c056858a",
+                "sha256:b69bc18418e72fcf359d17736cdc3f601a271203aff13ef7c57a415c8fd52ab0",
+                "sha256:ba43c42130d41352f32a5786c339cc931d05472ef7640fa3764d428dc294b88e",
+                "sha256:dd9d0ca112eb670dfa99a2fb7c4398bcc2a8c452dbf5507a8e4b5c4ae991bb2a",
+                "sha256:e3ce7c47be58897822d07f119461b2aa6aa41a53f3505bec517b14fc6c3611a6"
             ],
             "index": "pypi",
-            "version": "==1.1.11"
+            "version": "==1.1.12"
         },
         "packaging": {
             "hashes": [
@@ -281,71 +281,159 @@
         },
         "coverage": {
             "hashes": [
-                "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9",
-                "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f",
-                "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273",
-                "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994",
-                "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e",
-                "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50",
-                "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e",
-                "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e",
-                "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c",
-                "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853",
-                "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8",
-                "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8",
-                "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe",
-                "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165",
-                "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb",
-                "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59",
-                "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609",
-                "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18",
-                "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098",
-                "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd",
-                "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3",
-                "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43",
-                "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d",
-                "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359",
-                "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90",
-                "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78",
-                "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a",
-                "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99",
-                "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988",
-                "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2",
-                "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0",
-                "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694",
-                "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377",
-                "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d",
-                "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23",
-                "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312",
-                "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf",
-                "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6",
-                "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b",
-                "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c",
-                "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690",
-                "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a",
-                "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f",
-                "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4",
-                "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25",
-                "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd",
-                "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852",
-                "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0",
-                "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244",
-                "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315",
-                "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078",
-                "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0",
-                "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27",
-                "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132",
-                "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5",
-                "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247",
-                "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022",
-                "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b",
-                "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3",
-                "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18",
-                "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5",
-                "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"
+                "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95",
+                "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9",
+                "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe",
+                "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0",
+                "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924",
+                "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574",
+                "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702",
+                "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3",
+                "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b",
+                "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2",
+                "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea",
+                "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f",
+                "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3",
+                "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674",
+                "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9",
+                "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0",
+                "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e",
+                "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef",
+                "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb",
+                "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87",
+                "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1",
+                "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2",
+                "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703",
+                "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e",
+                "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd",
+                "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3",
+                "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4",
+                "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45",
+                "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa",
+                "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31",
+                "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8",
+                "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86",
+                "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6",
+                "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288",
+                "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf",
+                "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929",
+                "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc",
+                "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985",
+                "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3",
+                "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd",
+                "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e",
+                "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879",
+                "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57",
+                "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a",
+                "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad",
+                "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba",
+                "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d",
+                "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750",
+                "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c",
+                "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c",
+                "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f",
+                "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015",
+                "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558",
+                "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f",
+                "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d",
+                "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d",
+                "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425",
+                "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3",
+                "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953",
+                "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827",
+                "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c",
+                "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f",
+                "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73"
             ],
             "index": "pypi",
-            "version": "==7.6.10"
+            "version": "==7.6.12"
+        },
+        "docker": {
+            "hashes": [
+                "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c",
+                "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==7.1.0"
+        },
+        "greenlet": {
+            "hashes": [
+                "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e",
+                "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7",
+                "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01",
+                "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1",
+                "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159",
+                "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563",
+                "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83",
+                "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9",
+                "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395",
+                "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa",
+                "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942",
+                "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1",
+                "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441",
+                "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22",
+                "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9",
+                "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0",
+                "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba",
+                "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3",
+                "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1",
+                "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6",
+                "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291",
+                "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39",
+                "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d",
+                "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467",
+                "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475",
+                "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef",
+                "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c",
+                "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511",
+                "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c",
+                "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822",
+                "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a",
+                "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8",
+                "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d",
+                "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01",
+                "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145",
+                "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80",
+                "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13",
+                "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e",
+                "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b",
+                "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1",
+                "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef",
+                "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc",
+                "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff",
+                "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120",
+                "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437",
+                "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd",
+                "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981",
+                "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36",
+                "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a",
+                "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798",
+                "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7",
+                "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761",
+                "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0",
+                "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e",
+                "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af",
+                "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa",
+                "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c",
+                "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42",
+                "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e",
+                "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81",
+                "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e",
+                "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617",
+                "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc",
+                "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de",
+                "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111",
+                "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383",
+                "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70",
+                "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6",
+                "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4",
+                "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011",
+                "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803",
+                "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79",
+                "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"
+            ],
+            "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"
         },
         "idna": {
             "hashes": [
@@ -379,6 +467,14 @@
             "markers": "python_version >= '3.8'",
             "version": "==1.5.0"
         },
+        "pymysql": {
+            "hashes": [
+                "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c",
+                "sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==1.1.1"
+        },
         "pytest": {
             "hashes": [
                 "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6",
@@ -403,6 +499,91 @@
             "index": "pypi",
             "version": "==1.12.1"
         },
+        "sqlalchemy": {
+            "hashes": [
+                "sha256:0398361acebb42975deb747a824b5188817d32b5c8f8aba767d51ad0cc7bb08d",
+                "sha256:0561832b04c6071bac3aad45b0d3bb6d2c4f46a8409f0a7a9c9fa6673b41bc03",
+                "sha256:07258341402a718f166618470cde0c34e4cec85a39767dce4e24f61ba5e667ea",
+                "sha256:0a826f21848632add58bef4f755a33d45105d25656a0c849f2dc2df1c71f6f50",
+                "sha256:1052723e6cd95312f6a6eff9a279fd41bbae67633415373fdac3c430eca3425d",
+                "sha256:12d5b06a1f3aeccf295a5843c86835033797fea292c60e72b07bcb5d820e6dd3",
+                "sha256:12f5c9ed53334c3ce719155424dc5407aaa4f6cadeb09c5b627e06abb93933a1",
+                "sha256:2a0ef3f98175d77180ffdc623d38e9f1736e8d86b6ba70bff182a7e68bed7727",
+                "sha256:2f2951dc4b4f990a4b394d6b382accb33141d4d3bd3ef4e2b27287135d6bdd68",
+                "sha256:3868acb639c136d98107c9096303d2d8e5da2880f7706f9f8c06a7f961961149",
+                "sha256:386b7d136919bb66ced64d2228b92d66140de5fefb3c7df6bd79069a269a7b06",
+                "sha256:3d3043375dd5bbcb2282894cbb12e6c559654c67b5fffb462fda815a55bf93f7",
+                "sha256:3e35d5565b35b66905b79ca4ae85840a8d40d31e0b3e2990f2e7692071b179ca",
+                "sha256:402c2316d95ed90d3d3c25ad0390afa52f4d2c56b348f212aa9c8d072a40eee5",
+                "sha256:40310db77a55512a18827488e592965d3dec6a3f1e3d8af3f8243134029daca3",
+                "sha256:40e9cdbd18c1f84631312b64993f7d755d85a3930252f6276a77432a2b25a2f3",
+                "sha256:49aa2cdd1e88adb1617c672a09bf4ebf2f05c9448c6dbeba096a3aeeb9d4d443",
+                "sha256:57dd41ba32430cbcc812041d4de8d2ca4651aeefad2626921ae2a23deb8cd6ff",
+                "sha256:5dba1cdb8f319084f5b00d41207b2079822aa8d6a4667c0f369fce85e34b0c86",
+                "sha256:5e1d9e429028ce04f187a9f522818386c8b076723cdbe9345708384f49ebcec6",
+                "sha256:63178c675d4c80def39f1febd625a6333f44c0ba269edd8a468b156394b27753",
+                "sha256:6493bc0eacdbb2c0f0d260d8988e943fee06089cd239bd7f3d0c45d1657a70e2",
+                "sha256:64aa8934200e222f72fcfd82ee71c0130a9c07d5725af6fe6e919017d095b297",
+                "sha256:665255e7aae5f38237b3a6eae49d2358d83a59f39ac21036413fab5d1e810578",
+                "sha256:6db316d6e340f862ec059dc12e395d71f39746a20503b124edc255973977b728",
+                "sha256:70065dfabf023b155a9c2a18f573e47e6ca709b9e8619b2e04c54d5bcf193178",
+                "sha256:8455aa60da49cb112df62b4721bd8ad3654a3a02b9452c783e651637a1f21fa2",
+                "sha256:8b0ac78898c50e2574e9f938d2e5caa8fe187d7a5b69b65faa1ea4648925b096",
+                "sha256:8bf312ed8ac096d674c6aa9131b249093c1b37c35db6a967daa4c84746bc1bc9",
+                "sha256:92f99f2623ff16bd4aaf786ccde759c1f676d39c7bf2855eb0b540e1ac4530c8",
+                "sha256:9c8bcad7fc12f0cc5896d8e10fdf703c45bd487294a986903fe032c72201596b",
+                "sha256:9cd136184dd5f58892f24001cdce986f5d7e96059d004118d5410671579834a4",
+                "sha256:9eb4fa13c8c7a2404b6a8e3772c17a55b1ba18bc711e25e4d6c0c9f5f541b02a",
+                "sha256:a2bc4e49e8329f3283d99840c136ff2cd1a29e49b5624a46a290f04dff48e079",
+                "sha256:a5645cd45f56895cfe3ca3459aed9ff2d3f9aaa29ff7edf557fa7a23515a3725",
+                "sha256:a9afbc3909d0274d6ac8ec891e30210563b2c8bdd52ebbda14146354e7a69373",
+                "sha256:aa498d1392216fae47eaf10c593e06c34476ced9549657fca713d0d1ba5f7248",
+                "sha256:afd776cf1ebfc7f9aa42a09cf19feadb40a26366802d86c1fba080d8e5e74bdd",
+                "sha256:b335a7c958bc945e10c522c069cd6e5804f4ff20f9a744dd38e748eb602cbbda",
+                "sha256:b3c4817dff8cef5697f5afe5fec6bc1783994d55a68391be24cb7d80d2dbc3a6",
+                "sha256:b79ee64d01d05a5476d5cceb3c27b5535e6bb84ee0f872ba60d9a8cd4d0e6579",
+                "sha256:b87a90f14c68c925817423b0424381f0e16d80fc9a1a1046ef202ab25b19a444",
+                "sha256:bf89e0e4a30714b357f5d46b6f20e0099d38b30d45fa68ea48589faf5f12f62d",
+                "sha256:c058b84c3b24812c859300f3b5abf300daa34df20d4d4f42e9652a4d1c48c8a4",
+                "sha256:c09a6ea87658695e527104cf857c70f79f14e9484605e205217aae0ec27b45fc",
+                "sha256:c57b8e0841f3fce7b703530ed70c7c36269c6d180ea2e02e36b34cb7288c50c7",
+                "sha256:c9cea5b756173bb86e2235f2f871b406a9b9d722417ae31e5391ccaef5348f2c",
+                "sha256:cb39ed598aaf102251483f3e4675c5dd6b289c8142210ef76ba24aae0a8f8aba",
+                "sha256:e036549ad14f2b414c725349cce0772ea34a7ab008e9cd67f9084e4f371d1f32",
+                "sha256:e185ea07a99ce8b8edfc788c586c538c4b1351007e614ceb708fd01b095ef33e",
+                "sha256:e5a4d82bdb4bf1ac1285a68eab02d253ab73355d9f0fe725a97e1e0fa689decb",
+                "sha256:eae27ad7580529a427cfdd52c87abb2dfb15ce2b7a3e0fc29fbb63e2ed6f8120",
+                "sha256:ecef029b69843b82048c5b347d8e6049356aa24ed644006c9a9d7098c3bd3bfd",
+                "sha256:ee3bee874cb1fadee2ff2b79fc9fc808aa638670f28b2145074538d4a6a5028e",
+                "sha256:f0d3de936b192980209d7b5149e3c98977c3810d401482d05fb6d668d53c1c63",
+                "sha256:f53c0d6a859b2db58332e0e6a921582a02c1677cc93d4cbb36fdf49709b327b2",
+                "sha256:f9d57f1b3061b3e21476b0ad5f0397b112b94ace21d1f439f2db472e568178ae"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==2.0.38"
+        },
+        "testcontainers-core": {
+            "hashes": [
+                "sha256:69a8bf2ddb52ac2d03c26401b12c70db0453cced40372ad783d6dce417e52095"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==0.0.1rc1"
+        },
+        "testcontainers-mysql": {
+            "hashes": [
+                "sha256:d22894e0d8c7b4f7424afef99f713aa7e7a19ff987b7723aed863b9c478a2c91"
+            ],
+            "index": "pypi",
+            "version": "==0.0.1rc1"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
+                "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==4.12.2"
+        },
         "urllib3": {
             "hashes": [
                 "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
@@ -410,6 +591,91 @@
             ],
             "markers": "python_version >= '3.9'",
             "version": "==2.3.0"
+        },
+        "wrapt": {
+            "hashes": [
+                "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.2"
         }
     }
 }
diff --git a/dbrepo-auth-service/init/app.py b/dbrepo-auth-service/init/app.py
index 5e42003a192d4b6536ccd8f6c6ccc1be9dcedd6d..65252a5ccd615684c9533e3fddde0242c98f7e8a 100644
--- a/dbrepo-auth-service/init/app.py
+++ b/dbrepo-auth-service/init/app.py
@@ -42,8 +42,8 @@ def fetch() -> (str, str):
 def save(user_id: str, keycloak_id: str) -> None:
     conn = mariadb.connect(user=os.getenv('METADATA_USERNAME', 'root'),
                            password=os.getenv('METADATA_DB_PASSWORD', 'dbrepo'),
-                           host="metadata-db",
-                           port=3306,
+                           host=os.getenv('METADATA_HOST', 'metadata-db'),
+                           port=int(os.getenv('METADATA_PORT', '3306')),
                            database=os.getenv('METADATA_DB', 'dbrepo'))
     cursor = conn.cursor()
     cursor.execute(
diff --git a/dbrepo-auth-service/init/test/conftest.py b/dbrepo-auth-service/init/test/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..72cdcebe21899d7523b792d9d91965c704ba3aff
--- /dev/null
+++ b/dbrepo-auth-service/init/test/conftest.py
@@ -0,0 +1,51 @@
+import logging
+import os
+from os.path import abspath
+
+import mariadb
+import pytest
+from testcontainers.mysql import MySqlContainer
+
+schema_location = abspath("../../dbrepo-metadata-db/1_setup-schema.sql")
+
+
+@pytest.fixture(scope="session", autouse=True)
+def session(request):
+    """
+    Create one MariaDB container per test run only (root:dbrepo)
+    :param request: /
+    :return: The MariaDB container
+    """
+    logging.debug("[fixture] creating mariadb container")
+    container = (MySqlContainer(image="mariadb:11.3.2",
+                                MYSQL_DATABASE=os.getenv('METADATA_DB', 'dbrepo'),
+                                MYSQL_ROOT_PASSWORD=os.getenv('METADATA_DB_PASSWORD', 'dbrepo'))
+                 .with_env('MARIADB_DATABASE', os.getenv('METADATA_DB', 'dbrepo'))
+                 .with_env('MARIADB_ROOT_PASSWORD', os.getenv('METADATA_DB_PASSWORD', 'dbrepo'))
+                 .with_volume_mapping(schema_location, "/docker-entrypoint-initdb.d/1_setup-schema.sql"))
+    logging.debug("[fixture] starting mariadb container")
+    container.start()
+
+    os.environ['METADATA_HOST'] = '127.0.0.1'
+    os.environ['METADATA_PORT'] = container.get_exposed_port(3306)
+
+    # destructor
+    def stop_mariadb():
+        container.stop()
+
+    request.addfinalizer(stop_mariadb)
+    return container
+
+
+def execute_single_result(query: str) -> any:
+    conn = mariadb.connect(user=os.getenv('METADATA_USERNAME', 'root'),
+                           password=os.getenv('METADATA_DB_PASSWORD', 'dbrepo'),
+                           host=os.getenv('METADATA_HOST', 'metadata-db'),
+                           port=int(os.getenv('METADATA_PORT', '3306')),
+                           database=os.getenv('METADATA_DB', 'dbrepo'))
+    cursor = conn.cursor()
+    cursor.execute(query)
+    rows = cursor.fetchone()
+    conn.commit()
+    conn.close()
+    return rows
diff --git a/dbrepo-auth-service/init/test/test_integration_app.py b/dbrepo-auth-service/init/test/test_integration_app.py
new file mode 100644
index 0000000000000000000000000000000000000000..330578ef5222c25b4d8134e84958219fd3cfe117
--- /dev/null
+++ b/dbrepo-auth-service/init/test/test_integration_app.py
@@ -0,0 +1,14 @@
+import unittest
+
+from app import save
+from conftest import execute_single_result
+
+
+class AppUnitTest(unittest.TestCase):
+
+    def test_save_succeeds(self):
+        # test
+        save("7a0b4b7f-77cd-4f28-a665-2da443024621", "65408e0f-2990-42cd-9533-a667c85b46b3")
+        user_id, keycloak_id = execute_single_result("SELECT id, keycloak_id FROM mdb_users LIMIT 1")
+        self.assertEqual("7a0b4b7f-77cd-4f28-a665-2da443024621", user_id)
+        self.assertEqual("65408e0f-2990-42cd-9533-a667c85b46b3", keycloak_id)
diff --git a/dbrepo-auth-service/init/test/test_unit_app.py b/dbrepo-auth-service/init/test/test_unit_app.py
index af6aed379a3780157718d760bdabd79475e8d249..4fb38ac2f45220d74f79ccbb12bc650f1cba4e3f 100644
--- a/dbrepo-auth-service/init/test/test_unit_app.py
+++ b/dbrepo-auth-service/init/test/test_unit_app.py
@@ -101,3 +101,36 @@ class AppUnitTest(unittest.TestCase):
                 fetch()
             except ImportError:
                 pass
+
+    def test_fetch_user_malformed_attr_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post(f'{endpoint}/realms/master/protocol/openid-connect/token', json=self.token_res, status_code=200)
+            mock.get(f'{endpoint}/admin/realms/dbrepo/users/?username=admin', json=[{
+                "id": "5b516520-67cb-4aa0-86a6-d12f8b8f1a20",
+                "attributes": {
+                    "LDAP_ID": []
+                }
+            }], status_code=200)
+
+            # test
+            try:
+                fetch()
+            except EnvironmentError:
+                pass
+
+    def test_fetch_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post(f'{endpoint}/realms/master/protocol/openid-connect/token', json=self.token_res, status_code=200)
+            mock.get(f'{endpoint}/admin/realms/dbrepo/users/?username=admin', json=[{
+                "id": "5b516520-67cb-4aa0-86a6-d12f8b8f1a20",
+                "attributes": {
+                    "LDAP_ID": ["7a0b4b7f-77cd-4f28-a665-2da443024621"]
+                }
+            }], status_code=200)
+
+            # test
+            ldap_user_id, user_id = fetch()
+            self.assertEqual("7a0b4b7f-77cd-4f28-a665-2da443024621", ldap_user_id)
+            self.assertEqual("5b516520-67cb-4aa0-86a6-d12f8b8f1a20", user_id)
diff --git a/dbrepo-auth-service/listeners/target/create-event-listener.jar b/dbrepo-auth-service/listeners/target/create-event-listener.jar
index 7b332886dd5aca385115460c47692fdef8522e0b..f370c8825750a431296ef68c3d482a2e4eee9389 100644
Binary files a/dbrepo-auth-service/listeners/target/create-event-listener.jar and b/dbrepo-auth-service/listeners/target/create-event-listener.jar differ
diff --git a/dbrepo-metadata-service/metrics.md b/dbrepo-metadata-service/metrics.md
index f3e0a3130f23e149a54f1eca8247c559443809da..2c244804b92db4e1519898d2ec6b78393dee5168 100644
--- a/dbrepo-metadata-service/metrics.md
+++ b/dbrepo-metadata-service/metrics.md
@@ -58,7 +58,6 @@
 | `dbrepo_user_create`               | Create user                   |
 | `dbrepo_user_find`                 | Get user                      |
 | `dbrepo_user_modify`               | Update user                   |
-| `dbrepo_user_password_modify`      | Update user password          |
 | `dbrepo_users_list`                | List users                    |
 | `dbrepo_view_create`               | Create view                   |
 | `dbrepo_view_delete`               | Delete view                   |
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 cfbf858000ae4b2e58be5999c3c98123cc70582c..cff05b8d6f1d670398703c12f79d86aefe8be1be 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
@@ -97,6 +97,7 @@ public interface MetadataMapper {
     DataTypeDto dataTypeToDataTypeDto(DataType data);
 
     @Mappings({
+            @Mapping(target = "id", ignore = true), /* id attribute is ignored by the library anyway, just making it explicit */
             @Mapping(target = "attributes", ignore = true)
     })
     UserRepresentation userCreateDtoToUserRepresentation(UserCreateDto data);
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 5ca14a5a34605e80ab48c5f2309f6dd15bea714f..3636aa63a300d62364ea11e1c77122b40053d0be 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
@@ -4,11 +4,11 @@ import at.tuwien.api.auth.CreateUserDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.api.user.UserBriefDto;
 import at.tuwien.api.user.UserDto;
-import at.tuwien.api.user.UserPasswordDto;
 import at.tuwien.api.user.UserUpdateDto;
-import at.tuwien.entities.database.Database;
 import at.tuwien.entities.user.User;
-import at.tuwien.exception.*;
+import at.tuwien.exception.AuthServiceException;
+import at.tuwien.exception.NotAllowedException;
+import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.mapper.MetadataMapper;
 import at.tuwien.service.AuthenticationService;
 import at.tuwien.service.DatabaseService;
@@ -212,60 +212,4 @@ public class UserEndpoint extends AbstractEndpoint {
                         userService.modify(user, data)));
     }
 
-    @PutMapping("/{userId}/password")
-    @Transactional(rollbackFor = {Exception.class})
-    @PreAuthorize("isAuthenticated()")
-    @Observed(name = "dbrepo_user_password_modify")
-    @Operation(summary = "Update user password",
-            description = "Updates password of user with id. Requires authentication.",
-            security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
-    @ApiResponses(value = {
-            @ApiResponse(responseCode = "202",
-                    description = "Modified user password"),
-            @ApiResponse(responseCode = "400",
-                    description = "Invalid password payload",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "403",
-                    description = "Not allowed to change foreign user password",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Failed to find database/user in metadata database",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "502",
-                    description = "Connection to auth service failed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "503",
-                    description = "Failed to get user in auth service",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-    })
-    public ResponseEntity<Void> password(@NotNull @PathVariable("userId") UUID userId,
-                                         @NotNull @Valid @RequestBody UserPasswordDto data,
-                                         @NotNull Principal principal) throws NotAllowedException,
-            UserNotFoundException, DatabaseNotFoundException, DataServiceException,
-            DataServiceConnectionException, AuthServiceException {
-        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");
-        }
-        for (Database database : databaseService.findAllAtLestReadAccess(userId)) {
-            databaseService.updatePassword(database, user);
-        }
-        userService.updatePassword(user, data);
-        authenticationService.setupFinished(user);
-        return ResponseEntity.accepted()
-                .build();
-    }
-
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java
index 44e35f3a29ba8ee89f440540da98d249ead8a13d..aca2f9f356840af623ac0443fa6392092a5f865a 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java
@@ -4,6 +4,7 @@ import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.exception.*;
 import com.auth0.jwt.exceptions.TokenExpiredException;
 import io.swagger.v3.oas.annotations.Hidden;
+import jakarta.ws.rs.NotAuthorizedException;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
@@ -31,6 +32,20 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
         return new ResponseEntity<>(response, headers, response.getStatus());
     }
 
+    @Hidden
+    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
+    @ExceptionHandler(NotAuthorizedException.class)
+    public ResponseEntity<ApiErrorDto> handle(NotAuthorizedException e) {
+        final HttpHeaders headers = new HttpHeaders();
+        headers.set("Content-Type", "application/problem+json");
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.UNAUTHORIZED)
+                .message(e.getLocalizedMessage())
+                .code("error.authentication.invalid")
+                .build();
+        return new ResponseEntity<>(response, headers, response.getStatus());
+    }
+
     @Hidden
     @ResponseStatus(code = HttpStatus.NOT_FOUND)
     @ExceptionHandler(AccessNotFoundException.class)
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 de5c8993a118471b786710a72fbc8799499d4a82..c69701e6dd649565e8070c8c2403b0867c1aed19 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
@@ -3,10 +3,11 @@ package at.tuwien.endpoints;
 import at.tuwien.api.auth.CreateUserDto;
 import at.tuwien.api.user.UserBriefDto;
 import at.tuwien.api.user.UserDto;
-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.AuthServiceException;
+import at.tuwien.exception.NotAllowedException;
+import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.service.AuthenticationService;
 import at.tuwien.service.DatabaseService;
 import at.tuwien.service.UserService;
@@ -32,7 +33,8 @@ import java.util.List;
 import java.util.UUID;
 
 import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
 
 @Log4j2
 @SpringBootTest
@@ -227,44 +229,6 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         modify_generic(USER_1_ID, USER_1, USER_1_PRINCIPAL, request);
     }
 
-    @Test
-    @WithAnonymousUser
-    public void password_anonymous_fails() {
-        final UserPasswordDto request = UserPasswordDto.builder()
-                .password(USER_1_PASSWORD)
-                .build();
-
-        /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            password_generic(null, request);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_4_USERNAME)
-    public void password_noRoleForeign_fails() {
-        final UserPasswordDto request = UserPasswordDto.builder()
-                .password(USER_1_PASSWORD)
-                .build();
-
-        /* test */
-        assertThrows(NotAllowedException.class, () -> {
-            password_generic(USER_4_PRINCIPAL, request);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME)
-    public void password_succeeds() throws NotAllowedException, DataServiceException, DataServiceConnectionException,
-            UserNotFoundException, DatabaseNotFoundException, AuthServiceException {
-        final UserPasswordDto request = UserPasswordDto.builder()
-                .password(USER_1_PASSWORD)
-                .build();
-
-        /* test */
-        password_generic(USER_1_PRINCIPAL, request);
-    }
-
     @Test
     @WithAnonymousUser
     public void create_anonymous_fails() {
@@ -364,30 +328,6 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         assertNotNull(body);
     }
 
-    protected void password_generic(Principal principal, UserPasswordDto data) throws NotAllowedException,
-            DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException,
-            AuthServiceException {
-
-        /* mock */
-        when(userService.findById(USER_1_ID))
-                .thenReturn(USER_1);
-        doNothing()
-                .when(authenticationService)
-                .setupFinished(USER_1);
-        doNothing()
-                .when(userService)
-                .updatePassword(USER_1, data);
-        when(databaseService.findAllAtLestReadAccess(USER_1_ID))
-                .thenReturn(List.of(DATABASE_1));
-        doNothing()
-                .when(databaseService)
-                .updatePassword(DATABASE_1, USER_1);
-
-        /* test */
-        final ResponseEntity<?> response = userEndpoint.password(USER_1_ID, data, principal);
-        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
-    }
-
     protected void generic_create(CreateUserDto data) {
 
         /* test */
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 daeb1c1a96340323c85ca15080a3bc9d2685823e..8db76d729ac1864ff0164ed1e3c9f5f1dfd01383 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
@@ -27,7 +27,6 @@ import org.springframework.web.client.RestTemplate;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.when;
 
@@ -316,6 +315,20 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
         });
     }
 
+    @Test
+    public void createDatabase_notFound_fails() {
+
+        /* mock */
+        doThrow(HttpClientErrorException.NotFound.class)
+                .when(dataServiceRestTemplate)
+                .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class));
+
+        /* test */
+        assertThrows(DatabaseNotFoundException.class, () -> {
+            dataServiceGateway.createDatabase(DATABASE_1_CREATE_INTERNAL);
+        });
+    }
+
     @Test
     public void updateDatabase_succeeds() throws DataServiceException, DataServiceConnectionException,
             DatabaseNotFoundException {
@@ -1103,7 +1116,8 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    public void getTableStatistics_emptyBody_fails() throws TableNotFoundException, DataServiceException, DataServiceConnectionException {
+    public void getTableStatistics_emptyBody_fails() throws TableNotFoundException, DataServiceException,
+            DataServiceConnectionException {
 
         /* mock */
         when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableStatisticDto.class)))
@@ -1114,4 +1128,87 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest {
         dataServiceGateway.getTableStatistics(DATABASE_3_ID, TABLE_8_ID);
     }
 
+    @Test
+    public void updateTable_succeeds() throws DataServiceException, DataServiceConnectionException,
+            DatabaseNotFoundException {
+
+        /* mock */
+        when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
+                .thenReturn(ResponseEntity.status(HttpStatus.ACCEPTED)
+                        .build());
+
+        /* test */
+        dataServiceGateway.updateTable(DATABASE_3_ID, TABLE_8_ID, TABLE_8_UPDATE_DTO);
+    }
+
+    @Test
+    public void updateTable_connection_fails() {
+
+        /* mock */
+        doThrow(HttpServerErrorException.class)
+                .when(dataServiceRestTemplate)
+                .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class));
+
+        /* test */
+        assertThrows(DataServiceConnectionException.class, () -> {
+            dataServiceGateway.updateTable(DATABASE_3_ID, TABLE_8_ID, TABLE_8_UPDATE_DTO);
+        });
+    }
+
+    @Test
+    public void updateTable_notFound_fails() {
+
+        /* mock */
+        doThrow(HttpClientErrorException.NotFound.class)
+                .when(dataServiceRestTemplate)
+                .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class));
+
+        /* test */
+        assertThrows(DatabaseNotFoundException.class, () -> {
+            dataServiceGateway.updateTable(DATABASE_3_ID, TABLE_8_ID, TABLE_8_UPDATE_DTO);
+        });
+    }
+
+    @Test
+    public void updateTable_malformed_fails() {
+
+        /* mock */
+        doThrow(HttpClientErrorException.BadRequest.class)
+                .when(dataServiceRestTemplate)
+                .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class));
+
+        /* test */
+        assertThrows(DataServiceException.class, () -> {
+            dataServiceGateway.updateTable(DATABASE_3_ID, TABLE_8_ID, TABLE_8_UPDATE_DTO);
+        });
+    }
+
+    @Test
+    public void updateTable_unauthorized_fails() {
+
+        /* mock */
+        doThrow(HttpClientErrorException.Unauthorized.class)
+                .when(dataServiceRestTemplate)
+                .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class));
+
+        /* test */
+        assertThrows(DataServiceException.class, () -> {
+            dataServiceGateway.updateTable(DATABASE_3_ID, TABLE_8_ID, TABLE_8_UPDATE_DTO);
+        });
+    }
+
+    @Test
+    public void updateTable_responseCode_fails() {
+
+        /* mock */
+        when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class)))
+                .thenReturn(ResponseEntity.status(HttpStatus.OK)
+                        .build());
+
+        /* test */
+        assertThrows(DataServiceException.class, () -> {
+            dataServiceGateway.updateTable(DATABASE_3_ID, TABLE_8_ID, TABLE_8_UPDATE_DTO);
+        });
+    }
+
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java
index 58701adfc34f3d8c1be0b189a857307737bcfc47..583fa4c44025dc035c4d300b3a9e24f0bb7b1790 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java
@@ -1,5 +1,6 @@
 package at.tuwien.gateway;
 
+import at.tuwien.exception.AuthServiceException;
 import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.gateway.impl.KeycloakGatewayImpl;
 import at.tuwien.test.AbstractUnitTest;
@@ -9,6 +10,7 @@ import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.DynamicPropertyRegistry;
@@ -18,7 +20,9 @@ import org.testcontainers.images.PullPolicy;
 import org.testcontainers.junit.jupiter.Container;
 import org.testcontainers.junit.jupiter.Testcontainers;
 
-import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.*;
 
 @Log4j2
 @SpringBootTest
@@ -92,4 +96,22 @@ public class KeycloakGatewayIntegrationTest extends AbstractUnitTest {
         });
     }
 
+    @Test
+    public void updateUser_succeeds() throws UserNotFoundException, AuthServiceException {
+
+        /* mock */
+        keycloakUtils.createUser(USER_1_ID, USER_1_KEYCLOAK_SIGNUP_REQUEST);
+
+        /* test */
+        keycloakGateway.updateUser(keycloakUtils.getUserId(USER_1_USERNAME), USER_1_UPDATE_DTO);
+        final UserRepresentation user = keycloakUtils.getUser(USER_1_USERNAME);
+        assertNotNull(user.getId());
+        assertEquals(USER_1_FIRSTNAME, user.getFirstName());
+        assertEquals(USER_1_LASTNAME, user.getLastName());
+        assertEquals(USER_1_THEME, user.firstAttribute("THEME"));
+        assertEquals(USER_1_ORCID_URL, user.firstAttribute("ORCID"));
+        assertEquals(USER_1_LANGUAGE, user.firstAttribute("LANGUAGE"));
+        assertEquals(USER_1_AFFILIATION, user.firstAttribute("AFFILIATION"));
+    }
+
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java
index 06a4e377b02ed73f36af6761832a381c25aef990..87d7d0185a6399c5b66340d892281053448e689a 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java
@@ -4,6 +4,7 @@ import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.exception.*;
 import at.tuwien.test.AbstractUnitTest;
 import com.auth0.jwt.exceptions.TokenExpiredException;
+import jakarta.ws.rs.NotAuthorizedException;
 import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -24,8 +25,7 @@ import java.util.Optional;
 
 import static at.tuwien.test.utils.EndpointUtils.getErrorCodes;
 import static at.tuwien.test.utils.EndpointUtils.getExceptions;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.*;
 
 @Log4j2
 @ExtendWith(SpringExtension.class)
@@ -51,7 +51,7 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
             Assertions.assertNotNull(exception.getDeclaredAnnotation(ResponseStatus.class).code());
             Assertions.assertNotEquals(exception.getDeclaredAnnotation(ResponseStatus.class).code(), HttpStatus.INTERNAL_SERVER_ERROR);
             Assertions.assertNotNull(exception.getDeclaredAnnotation(ResponseStatus.class).reason(), "Exception " + exception.getName() + " does not provide a reason code");
-            Assertions.assertTrue(errorCodes.contains(exception.getDeclaredAnnotation(ResponseStatus.class).reason()), "Exception code " + exception.getDeclaredAnnotation(ResponseStatus.class).reason() + " does have a reason code mapped in localized ui error messages");
+            assertTrue(errorCodes.contains(exception.getDeclaredAnnotation(ResponseStatus.class).reason()), "Exception code " + exception.getDeclaredAnnotation(ResponseStatus.class).reason() + " does have a reason code mapped in localized ui error messages");
             /* handler method */
             assertEquals(method.getDeclaredAnnotation(ResponseStatus.class).code(), exception.getDeclaredAnnotation(ResponseStatus.class).code());
         }
@@ -70,6 +70,19 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.token.expired", body.getCode());
     }
 
+    @Test
+    public void handle_notAuthorizedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new NotAuthorizedException("msg"));
+        assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertNotNull(body.getMessage());
+        assertEquals(HttpStatus.UNAUTHORIZED, body.getStatus());
+        assertEquals("error.authentication.invalid", body.getCode());
+    }
+
     @Test
     public void handle_accessNotFoundException_succeeds() {
 
@@ -135,7 +148,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.auth.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_brokerServiceConnectionException_succeeds() {
 
@@ -149,7 +161,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.broker.connection", body.getCode());
     }
 
-
     @Test
     public void handle_brokerServiceException_succeeds() {
 
@@ -163,7 +174,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.broker.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_conceptNotFoundException_succeeds() {
 
@@ -177,7 +187,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.concept.missing", body.getCode());
     }
 
-
     @Test
     public void handle_containerAlreadyExistsException_succeeds() {
 
@@ -191,7 +200,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.container.exists", body.getCode());
     }
 
-
     @Test
     public void handle_containerNotFoundException_succeeds() {
 
@@ -205,7 +213,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.container.missing", body.getCode());
     }
 
-
     @Test
     public void handle_containerQuotaException_succeeds() {
 
@@ -219,7 +226,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.container.quota", body.getCode());
     }
 
-
     @Test
     public void handle_credentialsInvalidException_succeeds() {
 
@@ -233,7 +239,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.user.credentials", body.getCode());
     }
 
-
     @Test
     public void handle_dataServiceConnectionException_succeeds() {
 
@@ -247,7 +252,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.data.connection", body.getCode());
     }
 
-
     @Test
     public void handle_dataServiceException_succeeds() {
 
@@ -261,7 +265,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.data.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_databaseMalformedException_succeeds() {
 
@@ -275,7 +278,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.database.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_databaseNotFoundException_succeeds() {
 
@@ -289,7 +291,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.database.missing", body.getCode());
     }
 
-
     @Test
     public void handle_databaseUnavailableException_succeeds() {
 
@@ -303,7 +304,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.database.connection", body.getCode());
     }
 
-
     @Test
     public void handle_doiNotFoundException_succeeds() {
 
@@ -317,7 +317,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.doi.missing", body.getCode());
     }
 
-
     @Test
     public void handle_emailExistsException_succeeds() {
 
@@ -331,7 +330,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.user.email-exists", body.getCode());
     }
 
-
     @Test
     public void handle_exchangeNotFoundException_succeeds() {
 
@@ -345,7 +343,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.exchange.missing", body.getCode());
     }
 
-
     @Test
     public void handle_externalServiceException_succeeds() {
 
@@ -359,7 +356,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.external.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_filterBadRequestException_succeeds() {
 
@@ -373,7 +369,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.semantic.filter", body.getCode());
     }
 
-
     @Test
     public void handle_formatNotAvailableException_succeeds() {
 
@@ -387,7 +382,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.identifier.format", body.getCode());
     }
 
-
     @Test
     public void handle_identifierNotFoundException_succeeds() {
 
@@ -401,7 +395,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.identifier.missing", body.getCode());
     }
 
-
     @Test
     public void handle_identifierNotSupportedException_succeeds() {
 
@@ -415,7 +408,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.identifier.unsupported", body.getCode());
     }
 
-
     @Test
     public void handle_imageAlreadyExistsException_succeeds() {
 
@@ -429,7 +421,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.image.exists", body.getCode());
     }
 
-
     @Test
     public void handle_imageInvalidException_succeeds() {
 
@@ -443,7 +434,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.image.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_imageNotFoundException_succeeds() {
 
@@ -457,7 +447,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.image.missing", body.getCode());
     }
 
-
     @Test
     public void handle_licenseNotFoundException_succeeds() {
 
@@ -471,7 +460,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.license.missing", body.getCode());
     }
 
-
     @Test
     public void handle_malformedException_succeeds() {
 
@@ -485,7 +473,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.request.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_messageNotFoundException_succeeds() {
 
@@ -499,7 +486,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.message.missing", body.getCode());
     }
 
-
     @Test
     public void handle_metadataServiceConnectionException_succeeds() {
 
@@ -513,7 +499,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.metadata.connection", body.getCode());
     }
 
-
     @Test
     public void handle_metadataServiceException_succeeds() {
 
@@ -527,7 +512,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.metadata.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_notAllowedException_succeeds() {
 
@@ -541,7 +525,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.request.forbidden", body.getCode());
     }
 
-
     @Test
     public void handle_ontologyNotFoundException_succeeds() {
 
@@ -555,7 +538,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.ontology.missing", body.getCode());
     }
 
-
     @Test
     public void handle_orcidNotFoundException_succeeds() {
 
@@ -569,7 +551,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.orcid.missing", body.getCode());
     }
 
-
     @Test
     public void handle_paginationException_succeeds() {
 
@@ -583,7 +564,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.request.pagination", body.getCode());
     }
 
-
     @Test
     public void handle_queryMalformedException_succeeds() {
 
@@ -597,7 +577,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.query.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_queryNotFoundException_succeeds() {
 
@@ -611,7 +590,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.query.missing", body.getCode());
     }
 
-
     @Test
     public void handle_queryNotSupportedException_succeeds() {
 
@@ -625,7 +603,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.query.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_queryStoreCreateException_succeeds() {
 
@@ -639,7 +616,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.store.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_queryStoreGCException_succeeds() {
 
@@ -653,7 +629,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.store.clean", body.getCode());
     }
 
-
     @Test
     public void handle_queryStoreInsertException_succeeds() {
 
@@ -667,7 +642,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.store.insert", body.getCode());
     }
 
-
     @Test
     public void handle_queryStorePersistException_succeeds() {
 
@@ -681,7 +655,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.store.persist", body.getCode());
     }
 
-
     @Test
     public void handle_queueNotFoundException_succeeds() {
 
@@ -695,7 +668,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.queue.missing", body.getCode());
     }
 
-
     @Test
     public void handle_remoteUnavailableException_succeeds() {
 
@@ -709,7 +681,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.metadata.privileged", body.getCode());
     }
 
-
     @Test
     public void handle_rorNotFoundException_succeeds() {
 
@@ -723,7 +694,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.ror.missing", body.getCode());
     }
 
-
     @Test
     public void handle_searchServiceConnectionException_succeeds() {
 
@@ -737,7 +707,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.search.connection", body.getCode());
     }
 
-
     @Test
     public void handle_searchServiceException_succeeds() {
 
@@ -751,7 +720,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.search.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_semanticEntityNotFoundException_succeeds() {
 
@@ -765,7 +733,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.semantic.missing", body.getCode());
     }
 
-
     @Test
     public void handle_sortException_succeeds() {
 
@@ -779,7 +746,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.request.sort", body.getCode());
     }
 
-
     @Test
     public void handle_storageNotFoundException_succeeds() {
 
@@ -793,7 +759,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.storage.missing", body.getCode());
     }
 
-
     @Test
     public void handle_storageUnavailableException_succeeds() {
 
@@ -807,7 +772,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.storage.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_tableExistsException_succeeds() {
 
@@ -821,7 +785,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.table.exists", body.getCode());
     }
 
-
     @Test
     public void handle_tableMalformedException_succeeds() {
 
@@ -835,7 +798,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.table.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_tableNotFoundException_succeeds() {
 
@@ -849,7 +811,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.table.missing", body.getCode());
     }
 
-
     @Test
     public void handle_tableSchemaException_succeeds() {
 
@@ -863,7 +824,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.schema.table", body.getCode());
     }
 
-
     @Test
     public void handle_unitNotFoundException_succeeds() {
 
@@ -877,7 +837,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.unit.missing", body.getCode());
     }
 
-
     @Test
     public void handle_uriMalformedException_succeeds() {
 
@@ -891,7 +850,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.semantics.uri", body.getCode());
     }
 
-
     @Test
     public void handle_userExistsException_succeeds() {
 
@@ -905,7 +863,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.user.exists", body.getCode());
     }
 
-
     @Test
     public void handle_userNotFoundException_succeeds() {
 
@@ -919,7 +876,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.user.missing", body.getCode());
     }
 
-
     @Test
     public void handle_viewMalformedException_succeeds() {
 
@@ -933,7 +889,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.view.invalid", body.getCode());
     }
 
-
     @Test
     public void handle_viewNotFoundException_succeeds() {
 
@@ -947,7 +902,6 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
         assertEquals("error.view.missing", body.getCode());
     }
 
-
     @Test
     public void handle_viewSchemaException_succeeds() {
 
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 790262c7399d36fb0d9a3cf6103f7899300daffb..7611a0a8b539124d14a66c0a1b5c7465cf8d8e7f 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
@@ -586,15 +586,9 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
         } catch (Exception e) {
             /* ignore */
         }
-        try {
-            userEndpoint.password(USER_1_ID, USER_1_PASSWORD_DTO, USER_1_PRINCIPAL);
-        } catch (Exception e) {
-            /* ignore */
-        }
 
         /* test */
-        for (String metric : List.of("dbrepo_users_list", "dbrepo_user_find", "dbrepo_user_modify",
-                "dbrepo_user_password_modify")) {
+        for (String metric : List.of("dbrepo_users_list", "dbrepo_user_find", "dbrepo_user_modify")) {
             assertThat(registry)
                     .hasObservationWithNameEqualTo(metric);
         }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java
index eb228ab3c3c3f781a585fa199b63c7a512d222c3..c224fa9a856e41e6c327fe6d05334fc953c91cea 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java
@@ -3,7 +3,6 @@ package at.tuwien.service;
 import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.AuthServiceException;
-import at.tuwien.exception.UserExistsException;
 import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.gateway.KeycloakGateway;
 import at.tuwien.repository.UserRepository;
@@ -124,20 +123,4 @@ public class UserServicePersistenceTest extends AbstractUnitTest {
             userService.findById(USER_2_ID);
         });
     }
-
-    @Test
-    public void validateUsernameNotExists_succeeds() throws UserExistsException {
-
-        /* test */
-        userService.validateUsernameNotExists(USER_2_USERNAME);
-    }
-
-    @Test
-    public void validateUsernameNotExists_fails() {
-
-        /* test */
-        assertThrows(UserExistsException.class, () -> {
-            userService.validateUsernameNotExists(USER_1_USERNAME);
-        });
-    }
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java
index df13d00b0858a28dd4ccc38c5542601790dc964a..c610f3ee95d6d8e78cc11a4b6ac826b5a296c80b 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java
@@ -1,5 +1,6 @@
 package at.tuwien.service;
 
+import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.AuthServiceException;
 import at.tuwien.exception.UserNotFoundException;
@@ -16,6 +17,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.UUID;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -26,10 +28,10 @@ import static org.mockito.Mockito.*;
 public class UserServiceUnitTest extends AbstractUnitTest {
 
     @MockBean
-    private KeycloakGateway keycloakGateway;
+    private UserRepository userRepository;
 
     @MockBean
-    private UserRepository userRepository;
+    private KeycloakGateway keycloakGateway;
 
     @Autowired
     private UserService userService;
@@ -85,6 +87,9 @@ public class UserServiceUnitTest extends AbstractUnitTest {
                 .thenReturn(Optional.of(USER_1));
         when(userRepository.save(any(User.class)))
                 .thenReturn(USER_1);
+        doNothing()
+                .when(keycloakGateway)
+                .updateUser(any(UUID.class), any(UserUpdateDto.class));
 
         /* test */
         final User response = userService.modify(USER_1, USER_1_UPDATE_DTO);
@@ -92,22 +97,6 @@ public class UserServiceUnitTest extends AbstractUnitTest {
         assertEquals(USER_1_USERNAME, response.getUsername());
     }
 
-    @Test
-    public void updatePassword_succeeds() throws UserNotFoundException, AuthServiceException {
-
-        /* mock */
-        doNothing()
-                .when(keycloakGateway)
-                .setupFinished(USER_1_ID);
-        when(userRepository.findById(USER_1_ID))
-                .thenReturn(Optional.of(USER_1));
-        when(userRepository.save(any(User.class)))
-                .thenReturn(USER_1);
-
-        /* test */
-        userService.updatePassword(USER_1, USER_1_PASSWORD_DTO);
-    }
-
     @Test
     public void findByUsername_fails() {
 
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 b3612fcc0fc306892db006bd12aa6ef483cf45a9..100178a337fc142d9694c1ad5813d71b34c6423e 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
@@ -52,6 +52,16 @@ public class KeycloakUtils {
         return UUID.fromString(users.get(0).getId());
     }
 
+    public UserRepresentation getUser(String username) throws UserNotFoundException {
+        final List<UserRepresentation> users = keycloak.realm(keycloakConfig.getRealm())
+                .users()
+                .search(username);
+        if (users.isEmpty()) {
+            throw new UserNotFoundException("Failed to find user: " + username);
+        }
+        return users.get(0);
+    }
+
     public void deleteUser(String username) {
         final List<UserRepresentation> users = keycloak.realm(keycloakConfig.getRealm())
                 .users()
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/ApiTemplateInterceptor.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/ApiTemplateInterceptor.java
deleted file mode 100644
index 2ffd0f6a0f776699f47afe852574d7a0c08ba34c..0000000000000000000000000000000000000000
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/ApiTemplateInterceptor.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package at.tuwien.gateway;
-
-import org.springframework.stereotype.Service;
-
-@Service
-public interface ApiTemplateInterceptor {
-}
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java
index 296457b3d8ba1cd6e14e8a46be85a217b9cb8e13..9436db80cb4ccde9c03cccc64482b9279b1d6d81 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java
@@ -21,7 +21,5 @@ public interface KeycloakGateway {
      */
     void deleteUser(UUID id) throws UserNotFoundException;
 
-    void setupFinished(UUID id) throws AuthServiceException, UserNotFoundException;
-
     void updateUser(UUID id, UserUpdateDto data) throws AuthServiceException, UserNotFoundException;
 }
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/ApiTemplateInterceptorImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/ApiTemplateInterceptorImpl.java
deleted file mode 100644
index 8a2bf70637a25822c526da93ac89467660dd02e0..0000000000000000000000000000000000000000
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/ApiTemplateInterceptorImpl.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package at.tuwien.gateway.impl;
-
-import at.tuwien.gateway.ApiTemplateInterceptor;
-import org.springframework.http.HttpRequest;
-import org.springframework.http.MediaType;
-import org.springframework.http.client.ClientHttpRequestExecution;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.stereotype.Service;
-
-import java.io.IOException;
-import java.util.List;
-
-@Service
-public class ApiTemplateInterceptorImpl implements ApiTemplateInterceptor, ClientHttpRequestInterceptor {
-
-    @Override
-    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
-            throws IOException {
-        request.getHeaders().setAccept(List.of(MediaType.APPLICATION_JSON));
-        request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
-        return execution.execute(request, body);
-    }
-}
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java
index 270653ee8ffade88a9b96c24e2650843b21c5dd5..d088fb63ef7ce3085d49030f584123f71c3c9543 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java
@@ -76,28 +76,6 @@ public class KeycloakGatewayImpl implements KeycloakGateway {
         log.info("Deleted user {} at auth service", id);
     }
 
-    @Override
-    public void setupFinished(UUID id) throws AuthServiceException, UserNotFoundException {
-        final UserResource resource = keycloak.realm(keycloakConfig.getRealm())
-                .users()
-                .get(String.valueOf(id));
-        final UserRepresentation user;
-        try {
-            user = resource.toRepresentation();
-        } catch (NotFoundException e) {
-            log.error("Failed to update user setup: not found: {}", e.getMessage());
-            throw new UserNotFoundException("Failed to update user setup: not found", e);
-        }
-        user.singleAttribute("SETUP_FINISHED", "true");
-        try {
-            resource.update(user);
-        } catch (ForbiddenException e) {
-            log.error("Failed to update user setup: forbidden: {}", e.getMessage());
-            throw new AuthServiceException("Failed to update user setup: forbidden", e);
-        }
-        log.info("Updated user {} setup at auth service", id);
-    }
-
     @Override
     public void updateUser(UUID id, UserUpdateDto data) throws AuthServiceException, UserNotFoundException {
         final UserResource resource = keycloak.realm(keycloakConfig.getRealm())
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java
deleted file mode 100644
index f73f8bd0dad15596ffecea3cbe9f4a411efa9b35..0000000000000000000000000000000000000000
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package at.tuwien.interceptor;
-
-import at.tuwien.api.keycloak.TokenDto;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.http.*;
-import org.springframework.http.client.ClientHttpRequestExecution;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-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.io.IOException;
-
-@Log4j2
-public class KeycloakInterceptor implements ClientHttpRequestInterceptor {
-
-    private final String adminUsername;
-    private final String adminPassword;
-    private final String keycloakEndpoint;
-    private final RestTemplate restTemplate;
-
-    public KeycloakInterceptor(RestTemplate restTemplate, String adminUsername, String adminPassword,
-                               String keycloakEndpoint) {
-        this.adminUsername = adminUsername;
-        this.adminPassword = adminPassword;
-        this.keycloakEndpoint = keycloakEndpoint;
-        this.restTemplate = restTemplate;
-    }
-
-    @Override
-    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
-            throws IOException {
-        restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(keycloakEndpoint));
-        final HttpHeaders headers = new HttpHeaders();
-        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-        final MultiValueMap<String, String> payload = new LinkedMultiValueMap<>();
-        payload.add("username", adminUsername);
-        payload.add("password", adminPassword);
-        payload.add("grant_type", "password");
-        payload.add("client_id", "admin-cli");
-        final String path = "/realms/master/protocol/openid-connect/token";
-        log.trace("obtain admin token at endpoint {} with path {}", keycloakEndpoint, path);
-        final ResponseEntity<TokenDto> response;
-        try {
-            response = restTemplate.exchange(path, HttpMethod.POST, new HttpEntity<>(payload, headers), TokenDto.class);
-        } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) {
-            log.error("Failed to obtain admin token: {}", e.getMessage());
-            return execution.execute(request, body);
-        }
-        if (response.getBody() == null) {
-            return execution.execute(request, body);
-        }
-        request.getHeaders().set("Authorization", "Bearer " + response.getBody().getAccessToken());
-        return execution.execute(request, body);
-    }
-}
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java
index 3abe07a10a16b7b72ec144d6ddeb7ee32cd599b6..f93e81b69f650f88167ec9fb886ba10520b4c6a6 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java
@@ -16,13 +16,7 @@ public interface AuthenticationService {
      * @throws AuthServiceConnectionException The connection with the auth service could not be established.
      * @throws UserNotFoundException      The user was not found after creation in the auth database.
      */
-    void delete(User user) throws AuthServiceException, AuthServiceConnectionException, UserNotFoundException, CredentialsInvalidException;
+    void delete(User user) throws AuthServiceException, AuthServiceConnectionException, UserNotFoundException,
+            CredentialsInvalidException;
 
-    /**
-     * Updates the password of a user with given id.
-     *
-     * @param user The user.
-     * @throws UserNotFoundException      The user was not found after creation in the auth database.
-     */
-    void setupFinished(User user) throws UserNotFoundException, AuthServiceException;
 }
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 c6ca0ff21eb02986f30d42ec0b8727b368d5bfe4..f38268c000aa85a663bacbb51511aeae8ef197d1 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
@@ -57,21 +57,5 @@ public interface UserService {
      */
     User modify(User user, UserUpdateDto data) throws UserNotFoundException, AuthServiceException;
 
-    /**
-     * Updates the user password for a user with given id in the metadata database.
-     *
-     * @param user The user.
-     * @param data The new password.
-     */
-    void updatePassword(User user, UserPasswordDto data);
-
-    /**
-     * Validates if a user with the given username already exists in the metadata database.
-     *
-     * @param username The username.
-     * @throws UserExistsException The user with this username already exists.
-     */
-    void validateUsernameNotExists(String username) throws UserExistsException;
-
     String getMariaDbPassword(String password);
 }
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java
index dec3577886de7e4e0b60b015f72f73099a441065..e6bb056bfe31f23563acad6fe801021a830f9528 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java
@@ -25,9 +25,4 @@ public class AuthenticationServiceImpl implements AuthenticationService {
         keycloakGateway.deleteUser(user.getKeycloakId());
     }
 
-    @Override
-    public void setupFinished(User user) throws AuthServiceException, UserNotFoundException {
-        keycloakGateway.setupFinished(user.getKeycloakId());
-    }
-
 }
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 e79dd9bd842ac4cbb145f3b86f533962c23e2625..ad05c4fd469a381aca7b8f11bfd6d0e5b017b2ff 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,11 +1,9 @@
 package at.tuwien.service.impl;
 
 import at.tuwien.api.auth.CreateUserDto;
-import at.tuwien.api.user.UserPasswordDto;
 import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.AuthServiceException;
-import at.tuwien.exception.UserExistsException;
 import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.gateway.KeycloakGateway;
 import at.tuwien.repository.UserRepository;
@@ -100,21 +98,6 @@ public class UserServiceImpl implements UserService {
         return user;
     }
 
-    @Override
-    public void updatePassword(User user, UserPasswordDto data) {
-        user.setMariadbPassword(getMariaDbPassword(data.getPassword()));
-        /* update at metadata database */
-        userRepository.save(user);
-        log.info("Updated password of user with id: {}", user.getId());
-    }
-
-    @Override
-    public void validateUsernameNotExists(String username) throws UserExistsException {
-        if (userRepository.existsByUsername(username)) {
-            throw new UserExistsException("User with username " + username + " already exists");
-        }
-    }
-
     @Override
     public String getMariaDbPassword(String password) {
         final byte[] utf8 = password.getBytes(StandardCharsets.UTF_8);
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 2921447017f7e2951d5c35ebbb5a53acc7eb7cff..7440de878ba3a1c6cceb74f1b36ef54c0f5b8e95 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
@@ -14,10 +14,7 @@ import at.tuwien.api.container.image.*;
 import at.tuwien.api.database.*;
 import at.tuwien.api.database.query.QueryBriefDto;
 import at.tuwien.api.database.query.QueryDto;
-import at.tuwien.api.database.table.CreateTableDto;
-import at.tuwien.api.database.table.TableBriefDto;
-import at.tuwien.api.database.table.TableDto;
-import at.tuwien.api.database.table.TableStatisticDto;
+import at.tuwien.api.database.table.*;
 import at.tuwien.api.database.table.columns.*;
 import at.tuwien.api.database.table.columns.concepts.*;
 import at.tuwien.api.database.table.constraints.ConstraintsDto;
@@ -2324,6 +2321,12 @@ public abstract class BaseTest {
             .owner(USER_1_BRIEF_DTO)
             .build();
 
+    public static final TableUpdateDto TABLE_8_UPDATE_DTO = TableUpdateDto.builder()
+            .description(TABLE_8_DESCRIPTION)
+            .isPublic(true)
+            .isSchemaPublic(true)
+            .build();
+
     public static final TableBriefDto TABLE_8_BRIEF_DTO = TableBriefDto.builder()
             .id(TABLE_8_ID)
             .databaseId(DATABASE_3_ID)
diff --git a/dbrepo-search-service/.gitignore b/dbrepo-search-service/.gitignore
index e4a1bfbd16200450d72d68d3c880e8f7aae5bae2..726e588cd1b2f59f2f847d48bf8a48549202d1b4 100644
--- a/dbrepo-search-service/.gitignore
+++ b/dbrepo-search-service/.gitignore
@@ -38,6 +38,12 @@ MANIFEST
 *.manifest
 *.spec
 
+# generate
+/friendly_names_overrides.json
+/test/rsa
+/test/test_keycloak_client.py
+/test/test_opensearch_client.py
+
 # Installer logs
 pip-log.txt
 pip-delete-this-directory.txt
diff --git a/dbrepo-search-service/Dockerfile b/dbrepo-search-service/Dockerfile
index 0efe76bc88c90ff59838ed52d44c24992ea4ce79..3e46d8fc90475c9dff854f1e6a6bbe8cf221a416 100644
--- a/dbrepo-search-service/Dockerfile
+++ b/dbrepo-search-service/Dockerfile
@@ -19,9 +19,9 @@ WORKDIR /app
 
 COPY --chown=1001 ./init/clients ./clients
 COPY --chown=1001 ./init/omlib ./omlib
+COPY --chown=1001 ./init/friendly_names_overrides.json ./friendly_names_overrides.json
 COPY --chown=1001 ./os-yml ./os-yml
 COPY --chown=1001 ./app.py ./app.py
-COPY --chown=1001 ./friendly_names_overrides.json ./friendly_names_overrides.json
 
 # non-root port
 EXPOSE 8080
diff --git a/dbrepo-search-service/app.py b/dbrepo-search-service/app.py
index f9e2dbcc77ac11a6f7c8175e2b0d868f848c98af..13a9be870405ad27463cb5e21681f76dd82947aa 100644
--- a/dbrepo-search-service/app.py
+++ b/dbrepo-search-service/app.py
@@ -186,7 +186,7 @@ app.config["METADATA_SERVICE_ENDPOINT"] = os.getenv("METADATA_SERVICE_ENDPOINT",
 app.config["JWT_ALGORITHM"] = "HS256"
 app.config["JWT_PUBKEY"] = '-----BEGIN PUBLIC KEY-----\n' + os.getenv("JWT_PUBKEY",
                                                                       "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB") + '\n-----END PUBLIC KEY-----'
-app.config["AUTH_SERVICE_ENDPOINT"] = os.getenv("AUTH_SERVICE_ENDPOINT", "http://auth-service:8080/api/auth")
+app.config["AUTH_SERVICE_ENDPOINT"] = os.getenv("AUTH_SERVICE_ENDPOINT", "http://auth-service:8080")
 app.config["AUTH_SERVICE_CLIENT"] = os.getenv("AUTH_SERVICE_CLIENT", "dbrepo-client")
 app.config["AUTH_SERVICE_CLIENT_SECRET"] = os.getenv("AUTH_SERVICE_CLIENT_SECRET", "MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG")
 app.config["OPENSEARCH_HOST"] = os.getenv('OPENSEARCH_HOST', 'search-db')
diff --git a/dbrepo-search-service/init/.coveragerc b/dbrepo-search-service/init/.coveragerc
new file mode 100644
index 0000000000000000000000000000000000000000..b5a0e606be380a3149f066712428e43c82af2bd8
--- /dev/null
+++ b/dbrepo-search-service/init/.coveragerc
@@ -0,0 +1,5 @@
+[report]
+
+omit =
+    # omit tests
+    ./test/*
\ No newline at end of file
diff --git a/dbrepo-search-service/init/Pipfile b/dbrepo-search-service/init/Pipfile
index b74ed7bc40da1da1c51c401b53a1da2676fb739e..171460cd231737e3dfb350a02db513b2a768deb2 100644
--- a/dbrepo-search-service/init/Pipfile
+++ b/dbrepo-search-service/init/Pipfile
@@ -9,12 +9,14 @@ opensearch-py = "~=2.2"
 python-dotenv = "~=1.0"
 testcontainers-opensearch = "*"
 pytest = "*"
-dbrepo = {path = "./lib/dbrepo-1.6.3.tar.gz"}
+dbrepo = {path = "./lib/dbrepo-1.6.4.tar.gz"}
 rdflib = "*"
 
 [dev-packages]
 coverage = "*"
 pytest = "*"
+requests-mock = "*"
+jwt = "~=1.3"
 
 [requires]
 python_version = "3.11"
diff --git a/dbrepo-search-service/init/Pipfile.lock b/dbrepo-search-service/init/Pipfile.lock
index 77017b6d15dcee3198686c31be302d1f4fbcd364..c565195173bc6a5fca334f50c962a38b582de8ba 100644
--- a/dbrepo-search-service/init/Pipfile.lock
+++ b/dbrepo-search-service/init/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "dac534d1eb6a0942c0e296c8a58491847c65d3ca23315039a3725591c86f694f"
+            "sha256": "e9b86cb78a55fb9906d294b2ce675933832658ad12ad1ed4f7f3d5893bc0a301"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -259,11 +259,9 @@
         },
         "dbrepo": {
             "hashes": [
-                "sha256:199a5e4d4ede04d871681880f3797a4bdbb09317ee3796df14c8c501756216d6",
-                "sha256:7d62d00d51c1f0a178c795a8cc09b3b4c93ee0a18aed6514e2cb60465cd877f7"
+                "sha256:a518aee79540d9e302b161e7e10072f50730489da19368f00a1e68204009ce44"
             ],
-            "markers": "python_version >= '3.11'",
-            "path": "./lib/dbrepo-1.6.3.tar.gz"
+            "path": "./lib/dbrepo-1.6.4.tar.gz"
         },
         "docker": {
             "hashes": [
@@ -585,64 +583,64 @@
         },
         "numpy": {
             "hashes": [
-                "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f",
-                "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0",
-                "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd",
-                "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2",
-                "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4",
-                "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648",
-                "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be",
-                "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb",
-                "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160",
-                "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd",
-                "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a",
-                "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84",
-                "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e",
-                "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748",
-                "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825",
-                "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60",
-                "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957",
-                "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715",
-                "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317",
-                "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e",
-                "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283",
-                "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278",
-                "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9",
-                "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de",
-                "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369",
-                "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb",
-                "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189",
-                "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014",
-                "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323",
-                "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e",
-                "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49",
-                "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50",
-                "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d",
-                "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37",
-                "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39",
-                "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576",
-                "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a",
-                "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba",
-                "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7",
-                "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826",
-                "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467",
-                "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495",
-                "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc",
-                "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391",
-                "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0",
-                "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97",
-                "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c",
-                "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac",
-                "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369",
-                "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8",
-                "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2",
-                "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff",
-                "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a",
-                "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df",
-                "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f"
+                "sha256:0391ea3622f5c51a2e29708877d56e3d276827ac5447d7f45e9bc4ade8923c52",
+                "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d",
+                "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693",
+                "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d",
+                "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8",
+                "sha256:1ad78ce7f18ce4e7df1b2ea4019b5817a2f6a8a16e34ff2775f646adce0a5027",
+                "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304",
+                "sha256:1f45315b2dc58d8a3e7754fe4e38b6fce132dab284a92851e41b2b344f6441c5",
+                "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5",
+                "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50",
+                "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a",
+                "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94",
+                "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021",
+                "sha256:39261798d208c3095ae4f7bc8eaeb3481ea8c6e03dc48028057d3cbdbdb8937e",
+                "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe",
+                "sha256:3c2ec8a0f51d60f1e9c0c5ab116b7fc104b165ada3f6c58abf881cb2eb16044d",
+                "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890",
+                "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8",
+                "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe",
+                "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1",
+                "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e",
+                "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b",
+                "sha256:596140185c7fa113563c67c2e894eabe0daea18cf8e33851738c19f70ce86aeb",
+                "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b",
+                "sha256:5ebeb7ef54a7be11044c33a17b2624abe4307a75893c001a4800857956b41094",
+                "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea",
+                "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c",
+                "sha256:77974aba6c1bc26e3c205c2214f0d5b4305bdc719268b93e768ddb17e3fdd636",
+                "sha256:783145835458e60fa97afac25d511d00a1eca94d4a8f3ace9fe2043003c678e4",
+                "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba",
+                "sha256:7c8dde0ca2f77828815fd1aedfdf52e59071a5bae30dac3b4da2a335c672149a",
+                "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d",
+                "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95",
+                "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2",
+                "sha256:95172a21038c9b423e68be78fd0be6e1b97674cde269b76fe269a5dfa6fadf0b",
+                "sha256:9f48ba6f6c13e5e49f3d3efb1b51c8193215c42ac82610a04624906a9270be6f",
+                "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1",
+                "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532",
+                "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082",
+                "sha256:c8b0451d2ec95010d1db8ca733afc41f659f425b7f608af569711097fd6014e2",
+                "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0",
+                "sha256:cbc6472e01952d3d1b2772b720428f8b90e2deea8344e854df22b0618e9cce71",
+                "sha256:cdfe0c22692a30cd830c0755746473ae66c4a8f2e7bd508b35fb3b6a0813d787",
+                "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef",
+                "sha256:d42f9c36d06440e34226e8bd65ff065ca0963aeecada587b937011efa02cdc9d",
+                "sha256:d5b47c440210c5d1d67e1cf434124e0b5c395eee1f5806fdd89b553ed1acd0a3",
+                "sha256:d9b4a8148c57ecac25a16b0e11798cbe88edf5237b0df99973687dd866f05e1b",
+                "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf",
+                "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020",
+                "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76",
+                "sha256:e37242f5324ffd9f7ba5acf96d774f9276aa62a966c0bad8dae692deebec7716",
+                "sha256:ed2cf9ed4e8ebc3b754d398cba12f24359f018b416c380f577bbae112ca52fc9",
+                "sha256:f2712c5179f40af9ddc8f6727f2bd910ea0eb50206daea75f58ddd9fa3f715bb",
+                "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610",
+                "sha256:f6b3dfc7661f8842babd8ea07e9897fe3d9b69a1d7e5fbb743e4160f9387833b"
             ],
-            "markers": "python_version == '3.11'",
-            "version": "==2.2.2"
+            "markers": "python_version >= '3.10'",
+            "version": "==2.2.3"
         },
         "opensearch-py": {
             "hashes": [
@@ -1045,7 +1043,7 @@
                 "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
                 "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"
             ],
-            "markers": "python_version >= '3.10'",
+            "markers": "python_version >= '3.9'",
             "version": "==2.3.0"
         },
         "werkzeug": {
@@ -1231,6 +1229,185 @@
         }
     },
     "develop": {
+        "certifi": {
+            "hashes": [
+                "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651",
+                "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==2025.1.31"
+        },
+        "cffi": {
+            "hashes": [
+                "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8",
+                "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2",
+                "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1",
+                "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15",
+                "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36",
+                "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824",
+                "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8",
+                "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36",
+                "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17",
+                "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf",
+                "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc",
+                "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3",
+                "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed",
+                "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702",
+                "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1",
+                "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8",
+                "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903",
+                "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6",
+                "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d",
+                "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b",
+                "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e",
+                "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be",
+                "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c",
+                "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683",
+                "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9",
+                "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c",
+                "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8",
+                "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1",
+                "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4",
+                "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655",
+                "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67",
+                "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595",
+                "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0",
+                "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65",
+                "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41",
+                "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6",
+                "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401",
+                "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6",
+                "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3",
+                "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16",
+                "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93",
+                "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e",
+                "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4",
+                "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964",
+                "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c",
+                "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576",
+                "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0",
+                "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3",
+                "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662",
+                "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3",
+                "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff",
+                "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5",
+                "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd",
+                "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f",
+                "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5",
+                "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14",
+                "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d",
+                "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9",
+                "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7",
+                "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382",
+                "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a",
+                "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e",
+                "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a",
+                "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4",
+                "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99",
+                "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
+                "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.17.1"
+        },
+        "charset-normalizer": {
+            "hashes": [
+                "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537",
+                "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa",
+                "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a",
+                "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294",
+                "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b",
+                "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd",
+                "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601",
+                "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd",
+                "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4",
+                "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d",
+                "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2",
+                "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313",
+                "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd",
+                "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa",
+                "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8",
+                "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1",
+                "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2",
+                "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496",
+                "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d",
+                "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b",
+                "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e",
+                "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a",
+                "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4",
+                "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca",
+                "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78",
+                "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408",
+                "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5",
+                "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3",
+                "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f",
+                "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a",
+                "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765",
+                "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6",
+                "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146",
+                "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6",
+                "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9",
+                "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd",
+                "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c",
+                "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f",
+                "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545",
+                "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176",
+                "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770",
+                "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824",
+                "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f",
+                "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf",
+                "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487",
+                "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d",
+                "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd",
+                "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b",
+                "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534",
+                "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f",
+                "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b",
+                "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9",
+                "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd",
+                "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125",
+                "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9",
+                "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de",
+                "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11",
+                "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d",
+                "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35",
+                "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f",
+                "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda",
+                "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7",
+                "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a",
+                "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971",
+                "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8",
+                "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41",
+                "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d",
+                "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f",
+                "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757",
+                "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a",
+                "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886",
+                "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77",
+                "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76",
+                "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247",
+                "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85",
+                "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb",
+                "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7",
+                "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e",
+                "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6",
+                "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037",
+                "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1",
+                "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e",
+                "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807",
+                "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407",
+                "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c",
+                "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12",
+                "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3",
+                "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089",
+                "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd",
+                "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e",
+                "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00",
+                "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==3.4.1"
+        },
         "coverage": {
             "hashes": [
                 "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95",
@@ -1301,6 +1478,51 @@
             "markers": "python_version >= '3.9'",
             "version": "==7.6.12"
         },
+        "cryptography": {
+            "hashes": [
+                "sha256:00918d859aa4e57db8299607086f793fa7813ae2ff5a4637e318a25ef82730f7",
+                "sha256:1e8d181e90a777b63f3f0caa836844a1182f1f265687fac2115fcf245f5fbec3",
+                "sha256:1f9a92144fa0c877117e9748c74501bea842f93d21ee00b0cf922846d9d0b183",
+                "sha256:21377472ca4ada2906bc313168c9dc7b1d7ca417b63c1c3011d0c74b7de9ae69",
+                "sha256:24979e9f2040c953a94bf3c6782e67795a4c260734e5264dceea65c8f4bae64a",
+                "sha256:2a46a89ad3e6176223b632056f321bc7de36b9f9b93b2cc1cccf935a3849dc62",
+                "sha256:322eb03ecc62784536bc173f1483e76747aafeb69c8728df48537eb431cd1911",
+                "sha256:436df4f203482f41aad60ed1813811ac4ab102765ecae7a2bbb1dbb66dcff5a7",
+                "sha256:4f422e8c6a28cf8b7f883eb790695d6d45b0c385a2583073f3cec434cc705e1a",
+                "sha256:53f23339864b617a3dfc2b0ac8d5c432625c80014c25caac9082314e9de56f41",
+                "sha256:5fed5cd6102bb4eb843e3315d2bf25fede494509bddadb81e03a859c1bc17b83",
+                "sha256:610a83540765a8d8ce0f351ce42e26e53e1f774a6efb71eb1b41eb01d01c3d12",
+                "sha256:6c8acf6f3d1f47acb2248ec3ea261171a671f3d9428e34ad0357148d492c7864",
+                "sha256:6f76fdd6fd048576a04c5210d53aa04ca34d2ed63336d4abd306d0cbe298fddf",
+                "sha256:72198e2b5925155497a5a3e8c216c7fb3e64c16ccee11f0e7da272fa93b35c4c",
+                "sha256:887143b9ff6bad2b7570da75a7fe8bbf5f65276365ac259a5d2d5147a73775f2",
+                "sha256:888fcc3fce0c888785a4876ca55f9f43787f4c5c1cc1e2e0da71ad481ff82c5b",
+                "sha256:8e6a85a93d0642bd774460a86513c5d9d80b5c002ca9693e63f6e540f1815ed0",
+                "sha256:94f99f2b943b354a5b6307d7e8d19f5c423a794462bde2bf310c770ba052b1c4",
+                "sha256:9b336599e2cb77b1008cb2ac264b290803ec5e8e89d618a5e978ff5eb6f715d9",
+                "sha256:a2d8a7045e1ab9b9f803f0d9531ead85f90c5f2859e653b61497228b18452008",
+                "sha256:b8272f257cf1cbd3f2e120f14c68bff2b6bdfcc157fafdee84a1b795efd72862",
+                "sha256:bf688f615c29bfe9dfc44312ca470989279f0e94bb9f631f85e3459af8efc009",
+                "sha256:d9c5b9f698a83c8bd71e0f4d3f9f839ef244798e5ffe96febfa9714717db7af7",
+                "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f",
+                "sha256:df978682c1504fc93b3209de21aeabf2375cb1571d4e61907b3e7a2540e83026",
+                "sha256:e403f7f766ded778ecdb790da786b418a9f2394f36e8cc8b796cc056ab05f44f",
+                "sha256:eb3889330f2a4a148abead555399ec9a32b13b7c8ba969b72d8e500eb7ef84cd",
+                "sha256:f4daefc971c2d1f82f03097dc6f216744a6cd2ac0f04c68fb935ea2ba2a0d420",
+                "sha256:f51f5705ab27898afda1aaa430f34ad90dc117421057782022edf0600bec5f14",
+                "sha256:fd0ee90072861e276b0ff08bd627abec29e32a53b2be44e41dbcdf87cbee2b00"
+            ],
+            "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
+            "version": "==44.0.1"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
+                "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==3.10"
+        },
         "iniconfig": {
             "hashes": [
                 "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
@@ -1309,6 +1531,14 @@
             "markers": "python_version >= '3.7'",
             "version": "==2.0.0"
         },
+        "jwt": {
+            "hashes": [
+                "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.6'",
+            "version": "==1.3.1"
+        },
         "packaging": {
             "hashes": [
                 "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759",
@@ -1325,6 +1555,14 @@
             "markers": "python_version >= '3.8'",
             "version": "==1.5.0"
         },
+        "pycparser": {
+            "hashes": [
+                "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
+                "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==2.22"
+        },
         "pytest": {
             "hashes": [
                 "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6",
@@ -1333,6 +1571,31 @@
             "index": "pypi",
             "markers": "python_version >= '3.8'",
             "version": "==8.3.4"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760",
+                "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==2.32.3"
+        },
+        "requests-mock": {
+            "hashes": [
+                "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563",
+                "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.5'",
+            "version": "==1.12.1"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
+                "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"
+            ],
+            "markers": "python_version >= '3.9'",
+            "version": "==2.3.0"
         }
     }
 }
diff --git a/dbrepo-search-service/init/clients/keycloak_client.py b/dbrepo-search-service/init/clients/keycloak_client.py
index 2e15d00a9b272233feb7bab4cf6166c63b151e04..e8c277601bb2ce4ec0108ae5cd4dab7c3b061960 100644
--- a/dbrepo-search-service/init/clients/keycloak_client.py
+++ b/dbrepo-search-service/init/clients/keycloak_client.py
@@ -1,10 +1,10 @@
 import logging
+import os
 from dataclasses import dataclass
 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
 
@@ -20,13 +20,13 @@ class KeycloakClient:
 
     def obtain_user_token(self, username: str, password: str) -> str:
         response = requests.post(
-            f"{current_app.config['AUTH_SERVICE_ENDPOINT']}/realms/dbrepo/protocol/openid-connect/token",
+            f"{os.getenv('AUTH_SERVICE_ENDPOINT', 'http://auth-service:8080')}/realms/dbrepo/protocol/openid-connect/token",
             data={
                 "username": username,
                 "password": password,
                 "grant_type": "password",
-                "client_id": current_app.config["AUTH_SERVICE_CLIENT"],
-                "client_secret": current_app.config["AUTH_SERVICE_CLIENT_SECRET"]
+                "client_id": os.getenv("AUTH_SERVICE_CLIENT", "dbrepo-client"),
+                "client_secret": os.getenv("AUTH_SERVICE_CLIENT_SECRET", "MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG")
             })
         body = response.json()
         if "access_token" not in body:
@@ -34,7 +34,9 @@ class KeycloakClient:
         return response.json()["access_token"]
 
     def verify_jwt(self, access_token: str) -> ApiError | User:
-        public_key = jwk_from_pem(str(current_app.config["JWT_PUBKEY"]).encode('utf-8'))
+        public_key = jwk_from_pem(str('-----BEGIN PUBLIC KEY-----\n' + os.getenv("JWT_PUBKEY",
+                                                                                 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB") + '\n-----END PUBLIC KEY-----').encode(
+            'utf-8'))
         payload = JWT().decode(message=access_token, key=public_key, do_time_check=True)
         return User(id=payload.get('uid'), username=payload.get('client_id'),
                     roles=payload.get('realm_access')["roles"])
diff --git a/dbrepo-search-service/friendly_names_overrides.json b/dbrepo-search-service/init/friendly_names_overrides.json
similarity index 100%
rename from dbrepo-search-service/friendly_names_overrides.json
rename to dbrepo-search-service/init/friendly_names_overrides.json
diff --git a/dbrepo-search-service/test/rsa/rs256.key b/dbrepo-search-service/init/test/rsa/rs256.key
similarity index 100%
rename from dbrepo-search-service/test/rsa/rs256.key
rename to dbrepo-search-service/init/test/rsa/rs256.key
diff --git a/dbrepo-search-service/init/test/rsa/rsa/rs256.key b/dbrepo-search-service/init/test/rsa/rsa/rs256.key
new file mode 100644
index 0000000000000000000000000000000000000000..86b3eaf5c6c4c6b83071b6d1e9d69cb22bcd4085
--- /dev/null
+++ b/dbrepo-search-service/init/test/rsa/rsa/rs256.key
@@ -0,0 +1,3 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQABAoIBADNcMt6hAHub4JTAYS6Mra0EPRBO2XhWmACBrv3+8ETClXd5475KPLDewgRVtlmtbwU8G8awUXESQgPS9lfiqvQhPreA3cHlm6oP2WMKOEtakr2s8I+frsTBLCo0Ini9RaSzjoVVgS0zofyhASKi+T970MafSj5P3XNb8YBFdXgoYDiA7FXLH6a/+m7LScL+wGcFMAAeYESxZbMQLfH3v8L+4EcTraiwjLG17ZdlF3dpybMyUSse6ZQ/PdlyvBuzzLXhN6Ce2gd9ATfS+YWTzo7Yf+GU+ex5bIpVOfHqtuM/hyq7YGKENClsXwNZIAoFnvGCbvECAfgyapVrD30IfykCgYEA0rgsSZ82pxT40NxwgBD1g9lbNVBKXphRB/3S078qusUzJjT7AldEj4imGPhAbI7bI8gAeWJsp1XJWkjM8ktaVrh+NQl7p8e9OPh0pQF/5Bdg8ajbjXESpjnaU66pVYRQy/d+jNli/YRAHX5RUfsBl+6W4+WSVMGmKBiqJsur+ecCgYEAz1YVXClcmUnyZem5B+2E9noIzjF6ROE+jIb6rawM85P3Xd0lXtECQavtxw+Qk7I32qOwrxl1UpK2foVel3pazi+4OpMfmqtYGenRP1Zk1cZwrDo0cIemTDGjj3kJ8tYn12CGolFQpJZgK6OHzvG0tOxI5VZgjIViWNPe1PGWXtUCgYEAxXGNDe8BZs1f11S2lUlOw5yGug3hoYFXbAWJ5p7Ziuf8ZXB/QlJDC7se54a11wKEk6Jzz0lKRgE8CjzszJuOqnN0zn10QGIIC7nCklo1W6QMUmPGVWH994N976tZP6gbjQL6sT+AYcvpx7j0ubxYYeRNvnz+ACzzY964kGGHY0ECgYEAumlwPPNnMN7+VEjGNm2D7UMdJZ3wi3tkjF5ThdA5uMohTsAk+FG80KSu3RmOaGyEsUwY7+VYyYvlDm4E9PZqLBVVczyR3rMNPAcwPd0EPfvzk7WlLkOX7ct3fehaXH3VRlyfz9KCSeh1wOZ/lT1VtpD2nVOC7PSDzs92+kfXZZ0CgYAnrD1y4skgXkdwolZ3unn3EFyGm2d+X5aMTHwQPdWxqoNIAl/9wdghlzihwnPhhsxq1WzlxuC3V2IMrNPtRx70Mi+FbSmR5m4Xx5RptgMtMlwno+L40PzNJgMjHGjt0wcx3Vel8wuohDtnqMyS7P5nG1/TQx0Cyzwn7QOXlNpgbQ==
+-----END RSA PRIVATE KEY-----
\ No newline at end of file
diff --git a/dbrepo-search-service/test/rsa/rsa256.pkey b/dbrepo-search-service/init/test/rsa/rsa/rsa256.pkey
similarity index 100%
rename from dbrepo-search-service/test/rsa/rsa256.pkey
rename to dbrepo-search-service/init/test/rsa/rsa/rsa256.pkey
diff --git a/dbrepo-search-service/init/test/rsa/rsa256.pkey b/dbrepo-search-service/init/test/rsa/rsa256.pkey
new file mode 100644
index 0000000000000000000000000000000000000000..857dfb22beeac202c2955d7cc4f782b787492beb
--- /dev/null
+++ b/dbrepo-search-service/init/test/rsa/rsa256.pkey
@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/dbrepo-search-service/init/test/test_keycloak_client.py b/dbrepo-search-service/init/test/test_keycloak_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c43da230102f4d9c5a7401d7adbd6a9843e90f1
--- /dev/null
+++ b/dbrepo-search-service/init/test/test_keycloak_client.py
@@ -0,0 +1,53 @@
+import time
+import unittest
+
+import jwt
+import requests_mock
+
+from clients.keycloak_client import KeycloakClient
+
+
+class JwtTest(unittest.TestCase):
+
+    def response(self, username) -> dict:
+        return dict({
+            "client_id": username,
+            "access_token": "eyEY1234"
+        })
+
+    def token(self, username: str, roles: [str], iat: int = int(time.time())) -> str:
+        claims = {
+            'iat': iat,
+            'client_id': username,
+            'realm_access': {
+                'roles': roles
+            }
+        }
+        with open('test/rsa/rs256.key', 'rb') as fh:
+            return jwt.JWT().encode(claims, jwt.jwk_from_pem(fh.read()), alg='RS256')
+
+    def test_obtain_user_token_succeeds(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('http://auth-service:8080/realms/dbrepo/protocol/openid-connect/token',
+                      json=self.response("username"))
+            # test
+            token = KeycloakClient().obtain_user_token("username", "password")
+            self.assertEqual("eyEY1234", token)
+
+    def test_obtain_user_token_malformed_fails(self):
+        with requests_mock.Mocker() as mock:
+            # mock
+            mock.post('http://auth-service:8080/realms/dbrepo/protocol/openid-connect/token',
+                      json={"client_id": "username"})
+            # test
+            try:
+                KeycloakClient().obtain_user_token("username", "password")
+                self.fail()
+            except AssertionError:
+                pass
+
+    def test_verify_jwt_succeeds(self):
+        # test
+        user = KeycloakClient().verify_jwt(self.token("username", []))
+        self.assertEqual("username", user.username)
diff --git a/dbrepo-search-service/init/test/test_opensearch_client.py b/dbrepo-search-service/init/test/test_opensearch_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..70328df6387dd9d6743804bb3c240637a35d6f40
--- /dev/null
+++ b/dbrepo-search-service/init/test/test_opensearch_client.py
@@ -0,0 +1,206 @@
+import unittest
+
+import opensearchpy
+import os
+from dbrepo.api.dto import Database, Table, Column, ColumnType, Constraints, PrimaryKey, \
+    ConceptBrief, UnitBrief, UserBrief, ContainerBrief, ImageBrief, TableBrief, ColumnBrief
+from opensearchpy import NotFoundError
+
+from clients.opensearch_client import OpenSearchClient
+
+req = Database(id=1,
+               name="Test",
+               internal_name="test_tuw1",
+               owner=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
+               contact=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
+               exchange_name="dbrepo",
+               is_public=True,
+               is_schema_public=True,
+               container=ContainerBrief(id=1,
+                                        name="MariaDB",
+                                        internal_name="mariadb",
+                                        host="data-db",
+                                        image=ImageBrief(id=1,
+                                                         name="mariadb",
+                                                         version="11.1.3",
+                                                         jdbc_method="mariadb")),
+               tables=[Table(id=1,
+                             database_id=1,
+                             name="Data",
+                             internal_name="data",
+                             owner=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
+                             constraints=Constraints(uniques=[], foreign_keys=[], checks=[], primary_key=[]),
+                             is_versioned=False,
+                             queue_name="dbrepo",
+                             routing_key="dbrepo.1.1",
+                             is_public=True,
+                             is_schema_public=True,
+                             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)]
+                             )])
+
+
+class OpenSearchClientTest(unittest.TestCase):
+
+    def test_update_database_succeeds(self):
+        req.tables = [Table(id=1,
+                            name="Test Table",
+                            internal_name="test_table",
+                            queue_name="dbrepo",
+                            routing_key="dbrepo.test_tuw1.test_table",
+                            is_public=True,
+                            is_schema_public=True,
+                            database_id=req.id,
+                            constraints=Constraints(uniques=[], foreign_keys=[], checks=[],
+                                                    primary_key=[PrimaryKey(id=1,
+                                                                            table=TableBrief(id=1,
+                                                                                             database_id=req.id,
+                                                                                             name="Test Table",
+                                                                                             internal_name="test_table",
+                                                                                             is_public=True,
+                                                                                             is_schema_public=True,
+                                                                                             is_versioned=True,
+                                                                                             owned_by="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502"),
+                                                                            column=ColumnBrief(id=1,
+                                                                                               name="ID",
+                                                                                               database_id=req.id,
+                                                                                               table_id=1,
+                                                                                               internal_name="id",
+                                                                                               type=ColumnType.BIGINT))]),
+                            is_versioned=True,
+                            owner=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
+                            columns=[Column(id=1,
+                                            database_id=req.id,
+                                            table_id=1,
+                                            ord=0,
+                                            name="ID",
+                                            internal_name="id",
+                                            type=ColumnType.BIGINT,
+                                            is_null_allowed=False)])]
+        # mock
+        OpenSearchClient().update_database(database_id=req.id, data=req)
+
+        # test
+        database = OpenSearchClient().update_database(database_id=req.id, data=req)
+        self.assertEqual(1, database.id)
+        self.assertEqual("Test", database.name)
+        self.assertEqual("test_tuw1", database.internal_name)
+        self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.owner.id)
+        self.assertEqual("foo", database.owner.username)
+        self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.contact.id)
+        self.assertEqual("foo", database.contact.username)
+        self.assertEqual("dbrepo", database.exchange_name)
+        self.assertEqual(True, database.is_public)
+        self.assertEqual(1, database.container.id)
+        # ...
+        self.assertEqual(1, database.container.image.id)
+        # ...
+        self.assertEqual(1, len(database.tables))
+        self.assertEqual(1, database.tables[0].id)
+        self.assertEqual("Test Table", database.tables[0].name)
+        self.assertEqual("test_table", database.tables[0].internal_name)
+        self.assertEqual("dbrepo", database.tables[0].queue_name)
+        self.assertEqual("dbrepo.test_tuw1.test_table", database.tables[0].routing_key)
+        self.assertEqual(True, database.tables[0].is_public)
+        self.assertEqual(1, database.tables[0].database_id)
+        self.assertEqual(True, database.tables[0].is_versioned)
+        self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.tables[0].owner.id)
+        self.assertEqual("foo", database.tables[0].owner.username)
+        self.assertEqual(1, len(database.tables[0].columns))
+        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].type)
+        self.assertEqual(1, database.tables[0].columns[0].database_id)
+        self.assertEqual(1, database.tables[0].columns[0].table_id)
+        self.assertEqual(False, database.tables[0].columns[0].is_null_allowed)
+
+    def test_update_database_create_succeeds(self):
+        # test
+        database = OpenSearchClient().update_database(database_id=req.id, data=req)
+        self.assertEqual(1, database.id)
+        self.assertEqual("Test", database.name)
+        self.assertEqual("test_tuw1", database.internal_name)
+        self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.owner.id)
+        self.assertEqual("foo", database.owner.username)
+        self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.contact.id)
+        self.assertEqual("foo", database.contact.username)
+        self.assertEqual("dbrepo", database.exchange_name)
+        self.assertEqual(True, database.is_public)
+        self.assertEqual(1, database.container.id)
+        # ...
+        self.assertEqual(1, database.container.image.id)
+        # ...
+        self.assertEqual(1, len(database.tables))
+
+    def test_update_database_malformed_fails(self):
+        os.environ['OPENSEARCH_USERNAME'] = 'i_do_not_exist'
+
+        # test
+        try:
+            database = OpenSearchClient().update_database(database_id=req.id, data=req)
+        except opensearchpy.exceptions.TransportError:
+            pass
+
+    def test_delete_database_fails(self):
+
+        # test
+        try:
+            OpenSearchClient().delete_database(database_id=9999)
+        except opensearchpy.exceptions.NotFoundError:
+            pass
+
+    def test_delete_database_succeeds(self):
+        # mock
+        OpenSearchClient().update_database(database_id=req.id, data=req)
+
+        # test
+        OpenSearchClient().delete_database(database_id=req.id)
+
+    def test_get_fields_for_index_database_succeeds(self):
+        # mock
+        OpenSearchClient().update_database(database_id=req.id, data=req)
+
+        # test
+        response = OpenSearchClient().get_fields_for_index(field_type="database")
+        self.assertTrue(len(response) > 0)
+
+    def test_get_fields_for_index_user_succeeds(self):
+        # mock
+        OpenSearchClient().update_database(database_id=req.id, data=req)
+
+        # test
+        response = OpenSearchClient().get_fields_for_index(field_type="user")
+        self.assertTrue(len(response) > 0)
+
+    def test_fuzzy_search_succeeds(self):
+        # mock
+        OpenSearchClient().update_database(database_id=req.id, data=req)
+
+        # test
+        OpenSearchClient().fuzzy_search(search_term="test_tuw")
+
+    def test_unit_independent_search_fails(self):
+        # mock
+        OpenSearchClient().update_database(database_id=req.id, data=req)
+
+        # test
+        try:
+            OpenSearchClient().unit_independent_search(0, 100, {
+                "unit.uri": "http://www.ontology-of-units-of-measure.org/resource/om-2/degreeCelsius"})
+            self.fail()
+        except NotFoundError:
+            pass
diff --git a/dbrepo-search-service/test.sh b/dbrepo-search-service/test.sh
deleted file mode 100644
index 40328cd5dd490542e351668c7e59b33d0bba4965..0000000000000000000000000000000000000000
--- a/dbrepo-search-service/test.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-PIPENV_PIPFILE=./dbrepo-search-service/Pipfile
-source ./dbrepo-search-service/venv/bin/activate
-pip install pipenv
-pipenv install gunicorn && pipenv install --dev --system --deploy
-cd ./dbrepo-search-service/ && coverage run -m pytest test/test_app.py test/test_jwt.py test/test_opensearch_client.py test/test_keycloak_client.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt
-cat ./coverage.txt | grep -o 'TOTAL[^%]*%'
\ No newline at end of file
diff --git a/dbrepo-search-service/test/test_jwt.py b/dbrepo-search-service/test/test_jwt.py
index 96ce8410da4be0598fd6d635e7decc9950bd42e8..1f23853a7cbd6b6d30cdf29a09daafac012b7916 100644
--- a/dbrepo-search-service/test/test_jwt.py
+++ b/dbrepo-search-service/test/test_jwt.py
@@ -84,7 +84,7 @@ class JwtTest(unittest.TestCase):
         with app.app_context():
             with requests_mock.Mocker() as mock:
                 # mock
-                mock.post('http://auth-service:8080/api/auth/realms/dbrepo/protocol/openid-connect/token',
+                mock.post('http://auth-service:8080/realms/dbrepo/protocol/openid-connect/token',
                           json=self.response([]))
                 # test
                 user = verify_password('username', 'password')
diff --git a/dbrepo-search-service/test/test_keycloak_client.py b/dbrepo-search-service/test/test_keycloak_client.py
deleted file mode 100644
index 453a9b802be9885daa8e87afe265c272ee1ca211..0000000000000000000000000000000000000000
--- a/dbrepo-search-service/test/test_keycloak_client.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import time
-import unittest
-
-import jwt
-import requests_mock
-
-from app import app
-from clients.keycloak_client import KeycloakClient
-
-
-class JwtTest(unittest.TestCase):
-
-    def response(self, username) -> dict:
-        return dict({
-            "client_id": username,
-            "access_token": "eyEY1234"
-        })
-
-    def token(self, username: str, roles: [str], iat: int = int(time.time())) -> str:
-        claims = {
-            'iat': iat,
-            'client_id': username,
-            'realm_access': {
-                'roles': roles
-            }
-        }
-        with open('test/rsa/rs256.key', 'rb') as fh:
-            return jwt.JWT().encode(claims, jwt.jwk_from_pem(fh.read()), alg='RS256')
-
-    def test_obtain_user_token_succeeds(self):
-        with app.app_context():
-            with requests_mock.Mocker() as mock:
-                # mock
-                mock.post('http://auth-service:8080/api/auth/realms/dbrepo/protocol/openid-connect/token',
-                          json=self.response("username"))
-                # test
-                token = KeycloakClient().obtain_user_token("username", "password")
-                self.assertEqual("eyEY1234", token)
-
-    def test_obtain_user_token_malformed_fails(self):
-        with app.app_context():
-            with requests_mock.Mocker() as mock:
-                # mock
-                mock.post('http://auth-service:8080/api/auth/realms/dbrepo/protocol/openid-connect/token',
-                          json={"client_id": "username"})
-                # test
-                try:
-                    KeycloakClient().obtain_user_token("username", "password")
-                    self.fail()
-                except AssertionError:
-                    pass
-
-    def test_verify_jwt_succeeds(self):
-        with app.app_context():
-            # test
-            user = KeycloakClient().verify_jwt(self.token("username", []))
-            self.assertEqual("username", user.username)
diff --git a/dbrepo-search-service/test/test_opensearch_client.py b/dbrepo-search-service/test/test_opensearch_client.py
deleted file mode 100644
index edbdff683dff0f66319d87fb064fdeab6931bca0..0000000000000000000000000000000000000000
--- a/dbrepo-search-service/test/test_opensearch_client.py
+++ /dev/null
@@ -1,215 +0,0 @@
-import unittest
-
-import opensearchpy
-from dbrepo.api.dto import Database, Table, Column, ColumnType, Constraints, PrimaryKey, \
-    ConceptBrief, UnitBrief, UserBrief, ContainerBrief, ImageBrief, TableBrief, ColumnBrief
-from opensearchpy import NotFoundError
-
-from app import app
-from init.clients.opensearch_client import OpenSearchClient
-
-req = Database(id=1,
-               name="Test",
-               internal_name="test_tuw1",
-               owner=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
-               contact=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
-               exchange_name="dbrepo",
-               is_public=True,
-               is_schema_public=True,
-               container=ContainerBrief(id=1,
-                                        name="MariaDB",
-                                        internal_name="mariadb",
-                                        host="data-db",
-                                        image=ImageBrief(id=1,
-                                                         name="mariadb",
-                                                         version="11.1.3",
-                                                         jdbc_method="mariadb")),
-               tables=[Table(id=1,
-                             database_id=1,
-                             name="Data",
-                             internal_name="data",
-                             owner=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
-                             constraints=Constraints(uniques=[], foreign_keys=[], checks=[], primary_key=[]),
-                             is_versioned=False,
-                             queue_name="dbrepo",
-                             routing_key="dbrepo.1.1",
-                             is_public=True,
-                             is_schema_public=True,
-                             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)]
-                             )])
-
-
-class OpenSearchClientTest(unittest.TestCase):
-
-    def test_update_database_succeeds(self):
-        with app.app_context():
-            req.tables = [Table(id=1,
-                                name="Test Table",
-                                internal_name="test_table",
-                                queue_name="dbrepo",
-                                routing_key="dbrepo.test_tuw1.test_table",
-                                is_public=True,
-                                is_schema_public=True,
-                                database_id=req.id,
-                                constraints=Constraints(uniques=[], foreign_keys=[], checks=[],
-                                                        primary_key=[PrimaryKey(id=1,
-                                                                                table=TableBrief(id=1,
-                                                                                                 database_id=req.id,
-                                                                                                 name="Test Table",
-                                                                                                 internal_name="test_table",
-                                                                                                 is_public=True,
-                                                                                                 is_schema_public=True,
-                                                                                                 is_versioned=True,
-                                                                                                 owned_by="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502"),
-                                                                                column=ColumnBrief(id=1,
-                                                                                                   name="ID",
-                                                                                                   database_id=req.id,
-                                                                                                   table_id=1,
-                                                                                                   internal_name="id",
-                                                                                                   type=ColumnType.BIGINT))]),
-                                is_versioned=True,
-                                owner=UserBrief(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", username="foo"),
-                                columns=[Column(id=1,
-                                                database_id=req.id,
-                                                table_id=1,
-                                                ord=0,
-                                                name="ID",
-                                                internal_name="id",
-                                                type=ColumnType.BIGINT,
-                                                is_null_allowed=False)])]
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            database = OpenSearchClient().update_database(database_id=req.id, data=req)
-            self.assertEqual(1, database.id)
-            self.assertEqual("Test", database.name)
-            self.assertEqual("test_tuw1", database.internal_name)
-            self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.owner.id)
-            self.assertEqual("foo", database.owner.username)
-            self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.contact.id)
-            self.assertEqual("foo", database.contact.username)
-            self.assertEqual("dbrepo", database.exchange_name)
-            self.assertEqual(True, database.is_public)
-            self.assertEqual(1, database.container.id)
-            # ...
-            self.assertEqual(1, database.container.image.id)
-            # ...
-            self.assertEqual(1, len(database.tables))
-            self.assertEqual(1, database.tables[0].id)
-            self.assertEqual("Test Table", database.tables[0].name)
-            self.assertEqual("test_table", database.tables[0].internal_name)
-            self.assertEqual("dbrepo", database.tables[0].queue_name)
-            self.assertEqual("dbrepo.test_tuw1.test_table", database.tables[0].routing_key)
-            self.assertEqual(True, database.tables[0].is_public)
-            self.assertEqual(1, database.tables[0].database_id)
-            self.assertEqual(True, database.tables[0].is_versioned)
-            self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.tables[0].owner.id)
-            self.assertEqual("foo", database.tables[0].owner.username)
-            self.assertEqual(1, len(database.tables[0].columns))
-            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].type)
-            self.assertEqual(1, database.tables[0].columns[0].database_id)
-            self.assertEqual(1, database.tables[0].columns[0].table_id)
-            self.assertEqual(False, database.tables[0].columns[0].is_null_allowed)
-
-    def test_update_database_create_succeeds(self):
-        with app.app_context():
-            # test
-            database = OpenSearchClient().update_database(database_id=req.id, data=req)
-            self.assertEqual(1, database.id)
-            self.assertEqual("Test", database.name)
-            self.assertEqual("test_tuw1", database.internal_name)
-            self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.owner.id)
-            self.assertEqual("foo", database.owner.username)
-            self.assertEqual("c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", database.contact.id)
-            self.assertEqual("foo", database.contact.username)
-            self.assertEqual("dbrepo", database.exchange_name)
-            self.assertEqual(True, database.is_public)
-            self.assertEqual(1, database.container.id)
-            # ...
-            self.assertEqual(1, database.container.image.id)
-            # ...
-            self.assertEqual(1, len(database.tables))
-
-    def test_update_database_malformed_fails(self):
-        with app.app_context():
-            app.config['OPENSEARCH_USERNAME'] = 'i_do_not_exist'
-
-            # test
-            try:
-                database = OpenSearchClient().update_database(database_id=req.id, data=req)
-            except opensearchpy.exceptions.TransportError:
-                pass
-
-    def test_delete_database_fails(self):
-        with app.app_context():
-
-            # test
-            try:
-                OpenSearchClient().delete_database(database_id=9999)
-            except opensearchpy.exceptions.NotFoundError:
-                pass
-
-    def test_delete_database_succeeds(self):
-        with app.app_context():
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            OpenSearchClient().delete_database(database_id=req.id)
-
-    def test_get_fields_for_index_database_succeeds(self):
-        with app.app_context():
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            response = OpenSearchClient().get_fields_for_index(field_type="database")
-            self.assertTrue(len(response) > 0)
-
-    def test_get_fields_for_index_user_succeeds(self):
-        with app.app_context():
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            response = OpenSearchClient().get_fields_for_index(field_type="user")
-            self.assertTrue(len(response) > 0)
-
-    def test_fuzzy_search_succeeds(self):
-        with app.app_context():
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            OpenSearchClient().fuzzy_search(search_term="test_tuw")
-
-    def test_unit_independent_search_fails(self):
-        with app.app_context():
-            # mock
-            OpenSearchClient().update_database(database_id=req.id, data=req)
-
-            # test
-            try:
-                OpenSearchClient().unit_independent_search(0, 100, {
-                    "unit.uri": "http://www.ontology-of-units-of-measure.org/resource/om-2/degreeCelsius"})
-                self.fail()
-            except NotFoundError:
-                pass
diff --git a/dbrepo-ui/components/user/UserToolbar.vue b/dbrepo-ui/components/user/UserToolbar.vue
index 54210130d32f9c1cbe73378138e6c0db28c8cc88..59c0f53221ebf0faca4a102b7d81e200b95c6b63 100644
--- a/dbrepo-ui/components/user/UserToolbar.vue
+++ b/dbrepo-ui/components/user/UserToolbar.vue
@@ -11,9 +11,6 @@
           <v-tab
             :text="$t('toolbars.user.info')"
             to="/user/info" />
-          <v-tab
-            :text="$t('toolbars.user.authentication')"
-            to="/user/authentication" />
         </v-tabs>
       </template>
     </v-toolbar>
diff --git a/dbrepo-ui/pages/user/authentication.vue b/dbrepo-ui/pages/user/authentication.vue
deleted file mode 100644
index 912c1878c4940556e1dae3e890e4087ca7af5984..0000000000000000000000000000000000000000
--- a/dbrepo-ui/pages/user/authentication.vue
+++ /dev/null
@@ -1,138 +0,0 @@
-<template>
-  <div
-    v-if="loggedIn">
-    <UserToolbar />
-    <v-window v-model="tab">
-      <v-window-item>
-        <v-card
-          :title="$t('pages.settings.subpages.authentication.title')"
-          :subtitle="$t('pages.settings.subpages.authentication.subtitle')"
-          variant="flat"
-          rounded="0">
-          <v-card-text>
-            <v-form
-              v-model="valid2">
-              <v-row dense>
-                <v-col md="6">
-                  <v-text-field
-                    v-model="password"
-                    type="password"
-                    :rules="[v => !!v || $t('validation.required')]"
-                    required
-                    :variant="inputVariant"
-                    persistent-hint
-                    :label="$t('pages.settings.subpages.authentication.password.label')"
-                    :hint="$t('pages.settings.subpages.authentication.password.hint')" />
-                </v-col>
-              </v-row>
-              <v-row dense>
-                <v-col md="6">
-                  <v-text-field
-                    v-model="password2"
-                    type="password"
-                    :rules="[v => !!v || $t('validation.required'), v => (!!v && v) === password || $t('Not matching!')]"
-                    required
-                    :variant="inputVariant"
-                    persistent-hint
-                    :label="$t('pages.settings.subpages.authentication.confirm.label')"
-                    :hint="$t('pages.settings.subpages.authentication.confirm.hint')" />
-                </v-col>
-              </v-row>
-              <v-row>
-                <v-col md="6">
-                  <v-btn
-                    size="small"
-                    color="secondary"
-                    :loading="loadingUpdate"
-                    :disabled="!valid2"
-                    variant="flat"
-                    type="submit"
-                    :text="$t('pages.settings.subpages.authentication.submit.text')"
-                    @click="changePassword" />
-                </v-col>
-              </v-row>
-            </v-form>
-          </v-card-text>
-        </v-card>
-      </v-window-item>
-    </v-window>
-    <v-breadcrumbs :items="items" class="pa-0 mt-2" />
-  </div>
-</template>
-
-<script setup>
-const { loggedIn, user } = useOidcAuth()
-</script>
-<script>
-import UserToolbar from '@/components/user/UserToolbar.vue'
-import { useCacheStore } from '@/stores/cache.js'
-
-export default {
-  components: {
-    UserToolbar
-  },
-  data () {
-    return {
-      tab: 0,
-      valid1: false,
-      valid2: false,
-      loadingUpdate: false,
-      items: [
-        {
-          title: this.$t('navigation.user'),
-          to: '/user'
-        },
-        {
-          title: this.$t('toolbars.user.authentication'),
-          to: `/user/authentication`,
-          disabled: true
-        }
-      ],
-      email: null,
-      password: null,
-      password2: null,
-      cacheStore: useCacheStore()
-    }
-  },
-  computed: {
-    cacheUser () {
-      return this.cacheStore.getUser
-    },
-    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: {
-    changePassword () {
-      this.loadingUpdate = true
-      const userService = useUserService()
-      userService.updatePassword(this.cacheUser.uid, {'password': this.password})
-        .then(() => {
-          const user = Object.assign({}, this.cacheUser)
-          user.setup_finished = true
-          this.cacheStore.setUser(user)
-          // fixme [mweise]: currently nuxt-oidc-auth cannot refresh the session correctly
-          const toast = useToastInstance()
-          toast.success(this.$t('success.user.password'))
-          this.loadingUpdate = false
-        })
-        .catch(({code, message}) => {
-          const toast = useToastInstance()
-          if (typeof code !== 'string') {
-            return
-          }
-          toast.error(message)
-          this.loadingUpdate = false
-        })
-        .finally(() => {
-          this.loadingUpdate = false
-        })
-    }
-  }
-}
-</script>
diff --git a/helm/dbrepo/files/create-event-listener.jar b/helm/dbrepo/files/create-event-listener.jar
index 7b332886dd5aca385115460c47692fdef8522e0b..f370c8825750a431296ef68c3d482a2e4eee9389 100644
Binary files a/helm/dbrepo/files/create-event-listener.jar and b/helm/dbrepo/files/create-event-listener.jar differ
diff --git a/make/build.mk b/make/build.mk
index 270b2cee6fd80b9dea9a475399081ae34ad65c91..61f2f7448fb24d8ec5817a4a133c5b3e4680be1b 100644
--- a/make/build.mk
+++ b/make/build.mk
@@ -17,6 +17,7 @@ build-metadata-service: ## Build the Metadata Service.
 .PHONY: build-auth-event-listener
 build-auth-event-listener: ## Build the Auth Service Event Listener.
 	mvn -f ./dbrepo-auth-service/listeners/pom.xml clean package -DskipTests
+	cp ./dbrepo-auth-service/listeners/target/create-event-listener.jar ./helm/dbrepo/files/create-event-listener.jar
 
 .PHONY: build-ui
 build-ui: ## Build the UI.